JavaScript 中的 Callbacks、Promises和 Async/Await (上)

88 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

同步与异步

默认情况下,JavaScript 是一种同步的单线程编程语言。这意味着指令只能一个接一个地运行,而不是并行运行。考虑下面的小代码片段:

let a = 1;
let b = 2;
let sum = a + b;
console.log(sum);

上面的代码非常简单——它将两个数字相加,然后将总和记录到浏览器控制台。解释器按这个顺序一个接一个地执行这些指令,直到它完成。

但是这种方法也有缺点。假设我们想从数据库中获取大量数据,然后将其显示在我们的界面上。当解释器到达获取此数据的指令时,其余代码将被阻止执行,直到数据被获取并返回。

现在你可能会说要获取的数据并没有那么大,并且不会花费任何明显的时间。想象一下,你必须在多个不同点获取数据。这种延迟复合听起来不像是用户想要遇到的事情。

对我们来说幸运的是,通过引入异步 JavaScript 解决了同步 JavaScript 的问题。

将异步代码视为可以现在开始并稍后完成执行的代码。当 JavaScript 异步运行时,指令不一定像我们之前看到的那样一个接一个地执行。

为了正确实现这种异步行为,开发人员多年来使用了一些不同的解决方案。每个解决方案都对前一个解决方案进行了改进,这使得代码更加优化并且在变得复杂的情况下更易于理解。

为了进一步了解 JavaScript 的异步特性,我们将了解回调函数、promise 以及 async 和 await。

JavaScript 中的回调是什么?

回调是在另一个函数内部传递的函数,然后在该函数中调用以执行任务。

令人困惑?让我们通过实际实现它来分解它。

console.log('fired first');
console.log('fired second');

setTimeout(()=>{
    console.log('fired third');
},2000);

console.log('fired last');

上面的代码片段是一个将内容记录到控制台的小程序。但这里有一些新东西。解释器将执行第一条指令,然后执行第二条,但它会跳过第三条并执行最后一条。

setTimeout是一个带有两个参数的 JavaScript 函数。第一个参数是另一个函数,第二个参数是该函数应该执行的时间(以毫秒为单位)。现在你看到了回调的定义开始发挥作用。

本例中的函数setTimeout需要在两秒(2000 毫秒)后运行。想象一下,它被带到浏览器的某个单独部分执行,而其他指令继续执行。两秒后,返回函数的结果。

这就是为什么如果我们在程序中运行上面的代码片段,我们会得到:

fired first
fired second
fired last
fired third

你会看到在函数setTimeout返回结果之前记录了最后一条指令。假设我们使用此方法从数据库中获取数据。当用户在等待数据库调用返回结果时,执行中的流程不会被中断。

这种方法非常有效,但仅限于某一点。有时,开发人员必须在其代码中多次调用不同的源。为了进行这些调用,回调被嵌套,直到它们变得非常难以阅读或维护。这被称为回调地狱

为了解决这个问题,引入了 Promise。