JavaScript|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`);
});
});
外层 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
});
});
});
关于回调参数的出错处理 在上面的例子中,我们没有考虑错误。 如果脚本加载失败怎么办? 我们的回调应该能够对此做出反应。
这是一个改进的 loadScript 版本,可以跟踪加载错误:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = https://www.it610.com/article/src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
它调用 callback(null, script) 成功加载,否则调用 callback(error)。
消费代码:
loadScript('/my/script.js', function(error, script) {
if (error) {
// handle error
} else {
// script loaded successfully
}
});
我们用于 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 (*)
}
});
}
});
}
});
随着调用变得更加嵌套,代码变得更深并且越来越难以管理,特别是如果我们有真正的代码而不是......可能包括更多的循环、条件语句等。
这有时被称为
回调地狱
或 末日金字塔
。文章图片
嵌套调用的“金字塔”随着每个异步操作而向右增长。 很快它就失去了控制。
我们可以把每个匿名回调函数,改写成为由名称的标准函数,来部分程度的规避回调地狱问题:
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 (*)
}
}
【JavaScript|JavaScript 异步操作里的嵌套回调函数】我们使用 JavaScript 提供的 promise,可以从根本上规避回调地狱问题。
推荐阅读
- JavaScript|JavaScript Promise 的使用技巧
- 什么是|什么是 JavaScript 里的异步操作和回调函数
- 使用|使用 ABAP 编程语言的 System CALL 接口,直接执行 ABAP 服务器所在操作系统的 shell 命令
- matlab图像处理|matlab数字图像处理常用操作
- 计算机视觉|计算机视觉 图像基本操作
- opencv|图像基础入门--图像基本操作
- 数字图像MATLAB|TIT 数字图像处理 原理与实践(MATLAB) 入门教学 实验一 常用的MATLAB图像处理命令与图像基本操作
- python|opencv图像处理及视频处理基本操作
- emlog531常用的写表操作
- Node获取pnpm安装的包源码及代码路径操作