• JavaScript 异步操作里的嵌套回调函数


    嵌套回调函数经常用在两个逻辑上具有先后顺序的异步操作场景中。

    思考下面的问题:我们如何按顺序加载两个脚本?

    自然的解决方案是将第二个 loadScript 调用放在回调中,如下所示:

    loadScript('/my/script.js', function(script) {
    
      alert(`Cool, the ${script.src} is loaded, let's load one more`);
    
      loadScript('/my/script2.js', function(script) {
        alert(`Cool, the second script is loaded`);
      });
    
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    外层 loadScript 完成后,回调启动内层的 loadScript 调用。

    如果我们想要顺序加载更多的脚本,该怎么办?

    loadScript('/my/script.js', function(script) {
    
      loadScript('/my/script2.js', function(script) {
    
        loadScript('/my/script3.js', function(script) {
          // ...continue after all scripts are loaded
        });
    
      });
    
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    关于回调参数的出错处理

    在上面的例子中,我们没有考虑错误。 如果脚本加载失败怎么办? 我们的回调应该能够对此做出反应。

    这是一个改进的 loadScript 版本,可以跟踪加载错误:

    function loadScript(src, callback) {
      let script = document.createElement('script');
      script.src = src;
    
      script.onload = () => callback(null, script);
      script.onerror = () => callback(new Error(`Script load error for ${src}`));
    
      document.head.append(script);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    它调用 callback(null, script) 成功加载,否则调用 callback(error)。

    消费代码:

    loadScript('/my/script.js', function(error, script) {
      if (error) {
        // handle error
      } else {
        // script loaded successfully
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们用于 loadScript 的方法实际上很常见。 它被称为“错误优先回调(error-first callback)”风格。

    约定是:

    如果发生错误,回调的第一个参数是为错误保留的。 然后回调(错误)被调用。

    第二个参数(如果需要,还有下一个参数)用于成功的结果。 然后调用 callback(null, result1, result2…)。

    因此,单个回调函数用于报告错误和传回结果。

    Pyramid of Doom

    乍一看,嵌套回调函数看起来像是一种可行的异步编码方法。 确实如此。 对于一个或两个嵌套调用,其复杂度尚在编程人员能够掌控的范围内。

    但是对于一个接一个的多个异步操作,我们会有这样的代码:

    loadScript('1.js', function(error, script) {
    
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('2.js', function(error, script) {
          if (error) {
            handleError(error);
          } else {
            // ...
            loadScript('3.js', function(error, script) {
              if (error) {
                handleError(error);
              } else {
                // ...continue after all scripts are loaded (*)
              }
            });
    
          }
        });
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    随着调用变得更加嵌套,代码变得更深并且越来越难以管理,特别是如果我们有真正的代码而不是…可能包括更多的循环、条件语句等。

    这有时被称为 回调地狱末日金字塔

    嵌套调用的“金字塔”随着每个异步操作而向右增长。 很快它就失去了控制。

    我们可以把每个匿名回调函数,改写成为由名称的标准函数,来部分程度的规避回调地狱问题:

    loadScript('1.js', step1);
    
    function step1(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('2.js', step2);
      }
    }
    
    function step2(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('3.js', step3);
      }
    }
    
    function step3(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...continue after all scripts are loaded (*)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    我们使用 JavaScript 提供的 promise,可以从根本上规避回调地狱问题。

  • 相关阅读:
    Jmeter基础
    【递归、搜索与回溯算法】第四节.50. Pow(x, n)和2331. 计算布尔二叉树的值
    2022年全球市场中空玻璃密封胶总体规模、主要生产商、主要地区、产品和应用细分研究报告
    vue3基于vite打包
    基于ssm的关于汽车零部件加工的erp系统设计与实现-计算机毕业设计源码+LW文档
    计算机毕业设计django基于python网上拍卖系统
    JPA如何查询部分字段
    卷积版动作仿真模拟网络
    sovits远程访问设置
    拼多多商品详情api接口
  • 原文地址:https://blog.csdn.net/i042416/article/details/126302075