异步系列:回调
介绍
Async 是什么意思?
为什么会发生这种情况?
异步代码实际应用
进入心流
编写高阶函数
结论
介绍
JavaScript 初学者最容易犯的一个错误就是异步编程。本系列将揭秘 JavaScript 中异步编程的神秘面纱。
Async 是什么意思?
我们编写的大多数代码都是顺序或同步执行的。也就是说,先执行第 1 行,然后执行第 2 行,最后执行第 3 行。
例如,以下都是同步函数调用。一次只能执行其中一个。如果你正在洗澡,那么你可以放心地认为唤醒任务已经完全完成。
wakeup()
eatBreakfast()
takeShower()
然而,在 JavaScript 中,一些函数调用是并发执行的。这意味着浏览器实际上会在它们之间切换,使它们看起来像是同时进行的。
例如:从早上醒来的那一刻起,你开始观察世界。然后,你可能开始活动,并在某个时刻开始进食。你甚至可能同时进行这三项!注意,即使你在进食之前就开始观察世界,进食任务也会在观察之前完成。本质上,这就是异步函数调用的行为方式。
see()
move()
eat()
因此,一系列异步函数调用的执行结束顺序可能与启动顺序不同。对于需要特定顺序的新程序员来说,这常常会让他们感到沮丧。
为什么会发生这种情况?
虽然这不是一个硬性要求,但如果你想了解为什么会发生这种情况,你可以看看我关于 JavaScript 运行时的帖子。
异步代码实际应用
我有三个异步函数 printBlue/Green/Red。它们都会并发执行,但各自的执行时间不同。Green 最快,Red 最快,Blue 最慢。所以,如果像这样调用它们:
printBlue("Blue");
printGreen("Green");
printRed("Red");
输出:
输出为:绿色、红色、蓝色。下图直观地展示了这些函数的执行时间。
进入心流
由于它们各自的执行速度,这些函数的完成顺序与它们被调用的顺序不同,即打印“绿色”、“红色”然后“蓝色”。但是,我们想要打印“蓝色”、“绿色”然后“红色”,我们该如何解决这个问题?
在编程中,控制流是指强制执行语句顺序的概念。在 JavaScript 中,我们让开发者实现这一点的一种方法是让异步函数接受回调函数。
回调函数是我们传递给另一个函数 A(通常是异步的)的函数 C,这样只有当 A 的主要工作完成时,A 才会执行 C。
由于函数 A 具有另一个函数作为参数,因此它也被称为高阶函数。
幸运的是,我们的打印函数确实接受回调函数,所以我们可以创建一些辅助函数来实现我们想要的打印功能。然后,将辅助函数作为回调函数传递。
//helper function 1
function doPrintRed(){
//calls print red with our desired parameter
printRed('Red');
}
//helper function 2
function doPrintGreenRed(){
//calls printGreen with our desired parameter
//then passes doPrintRed as a callback
printGreen('Green', doPrintRed);
}
//call printBlue then passes do doPrintGreenRed as a callback
printBlue("Blue", doPrintGreenRed);
然而,这太长了。由于除了将辅助函数作为回调传递之外,我们没有任何用处,因此我们可以改用匿名函数。
匿名函数是没有名字的函数定义,可以在任何可以引用函数的地方编写。例如,我们可以在 doPrintGreen 中提供一个匿名函数,而不是编写 doPrintRed。
//helper function 2
function doPrintGreenRed(){
//calls printGreen with our desired parameter
//replace reference to doPrintRed with an anonymous function
printGreen('Green', function(){
//calls print red with our desired parameter
printRed('Red');
});
}
//call printBlue then passes do doPrintGreenRed as a callback
printBlue("Blue", doPrintGreenRed);
我们将 doPrintRed 的代码移到了一个匿名函数中,并将其作为回调传递给 printGreen。因此,printGreen 的第二个参数被称为匿名回调函数。
然后,您可以重复 doPrintGreenRed 的过程以实现以下目标。
//replace reference to doPrintGreenRed with an anonymous function
printBlue("Blue", function(){
//calls printGreen with our desired parameter
printGreen("Green", function(){
//calls print red with our desired parameter
printRed("Red");
});
});
传递的回调函数 printBlue() 调用 printGreen()。printGreen() 反过来也接收一个回调函数,然后该回调函数调用 printRed()。printBlue/Green/Red 的设计方式使得它们接收的回调仅在打印到屏幕后执行。
这是输出:
现在的执行看起来是这样的。
这是因为最内层函数必须等待外部函数执行,而外部函数必须等待另一个外部函数开始执行。
编写高阶函数
异步函数的作者负责设计该函数以接受回调,使用适当的值执行回调并通过文档进行解释。
下面是接收回调函数的高阶加法函数的简单实现。
function add(a, b, callback){//allow a callback to be passed
let answer = a + b;//perform calculation
callback(answer);//pass the output of the calculation to the callback
}
add(4, 5, function(result){
console.log("The result is", result);
});//Output: The result is 9
函数 add() 计算总和,并将结果传递给传递给 add() 的函数参数。高阶函数(例如 add())可能不会返回结果,而是要求将结果传递给一个函数。
示例代码可在此REPL中找到,因此您可以自己尝试一下。
结论
异步 JavaScript 的介绍到此结束。许多 JavaScript API 都是异步的,包括 fetch()。掌握这个基本概念将对您的学习之旅大有裨益。
文章来源:https://dev.to/snickdx/the-async-series-callbacks-5dc0