掌握异步JavaScript的秘诀是什么?

发表时间: 2023-11-20 07:00

JavaScript是一种同步(synchronous)且单线程的语言,那为什么又变成异步(asynchronous)了?

首先,异步JavaScript是一种允许代码并发运行而不阻塞其他代码执行的编程模式。相较于同步代码,它独立于其它代码去执行,从而可以提高Web应用程序的性能和响应速度。

其次,从用处上来讲,异步代码也特别有用,比如,我们代码经常需要与外部资源(例如,服务器数据库)交互,这可能会导致延迟并减慢代码的执行速度。通过使用异步技术,开发人员可以避免这些延迟,并允许其他代码在等待资源可用时继续执行。

接下来我们从如何实现如何执行两个方面来掌握异步JS。

一、异步JavaScript如何实现?

01. 使用回调

实现异步JavaScript的一种常见技术就是使用回调。回调是作为参数传递给另一个函数的函数,并在该函数完成后执行。

举个例子,代码如下:

这段代码,3秒后会在控制台打印结果,也就是说3秒延迟后会执行回调函数。同时,这3秒期间允许其他代码继续执行。

02. 使用Promise

实现异步JavaScript另一种技术是使用PromisePromise是一个对象,它表示异步操作的最终成功或失败,并提供处理该操作结果的机制。

举个例子,代码如下:

这段代码,我们使用构建函数创建了一个Promise,同时使用定时器,设置了3秒后成功返回Promise值,Promise通过then方法接收该返回值,同时,在此期间,允许其他代码继续执行。

03. 使用async/await

异步JavaScript还可以通过使用async/await语法来实现,这种方式提供了一种更简洁,更可读的异步代码编写方式。

举个例子,代码如下:

在这段代码中,我们使用async定义了一个函数,并使用构造函数创建了一个Promise,同样设置3秒后成功返回Promise值,然后,我们使用关键字await等待Promise返回 ,并将结果存储在变量result中。执行期间,允许函数之外的其他代码继续执行。

二、异步JavaScript如何执行?

编写JavaScript异步代码时,了解JavaScript运行时如何处理和执行任务至关重要。这就需要你充分理解调用堆栈事件循环Web API回调队列微任务队列等概念。

你也许已经看出来了,这些概念其实说的就是要掌握和理解事件循环的执行机制

关于JavaScript事件循环,我画了一张图,如下:

接下来对着这张图,我逐一讲一下这些概念。

01. 调用堆栈

调用堆栈是JavaScript用于管理函数调用的数据结构。它以后进先出 (LIFO) 为基础工作,这意味着最近添加的函数将首先执行。当一个函数被调用时,它被添加到堆栈的顶部。当函数返回时,它被从堆栈中删除。

关于堆栈的出入栈规则,我画了张图来理解。

图如下所示:

[入栈演示图]

[出栈演示图]

接下来我举个例子,帮大家详细理解一下具体代码的执行过程。

代码如下:

这段代码中,当bar函数被调用时,它被添加到调用堆栈的顶部。然后该bar函数调用该foo函数,该函数将添加到调用堆栈的顶部。当foo函数返回时,它会从堆栈中删除,然后是函数bar。

02. 事件循环

事件循环是JavaScript用于管理异步任务的机制。它会不断的检查任务队列,以查看是否有任何任务等待执行。如果有,它将任务添加到调用堆栈中。

同样,我们举个例子来讲一下

代码如下:

在这个例子中,console.log('start')console.log('end')语句被添加到调用堆栈并首先执行。setTimeout然后调用该函数,这是一个异步任务。该setTimeout功能已添加到Web API等待下一步往任务队列中推待执行的回调内容。

03. Web API

Web API是浏览器提供的一组API,允许JavaScript与浏览器环境进行交互。这些API包括setTimeout、setInterval和fetch函数。

当调用 Web API中的函数时,它会被添加到 Web APIs中。Web APIs管理任务并在完成时将其添加到任务队列。

04. 回调队列

回调队列是一种存储异步任务回调的数据结构。当异步任务完成时,其回调被添加到回调队列中。

队列(正向)的出入队规则如下:

上图很容易看懂,符合先进先出原则。下面我们举一个实例来看一下。

代码如下:

在这个实例中,setTimeout函数和Promise都是异步任务。当setTimeout函数完成时,其回调被添加到回调队列中。当Promise函数完成时,其回调将添加到微任务队列中。

05. 微任务队列

微任务队列与回调队列类似,但它用于微任务。微任务是当前任务完成后立即执行的函数。

同样,我举个例子来讲一下。

代码如下:

在这个例子中,console.log('end')语句被添加到调用堆栈并首先执行。Promise然后调用该函数,这是一个微任务。该Promise函数的回调被添加到微任务队列中,并在当前任务完成后立即执行。

三、实例分解执行过程

任务分:同步任务(宏微任务)和异步任务(宏微任务)

什么是事件循环?

执行完宏任务,执行宏任务的微任务

执行另一个宏任务,执行另一个宏任务的微任务

01. 先来看一张流程图

从上图看出,事件循环就是不断执行宏任务及宏任务中的微任务的过程。

02. 再来看一个例子

这段代码作为宏任务,进入主线程。执行过程如下

第一步,先遇到setTimeout,那么将其回调函数注册后分发到另一个宏任务

第二步,接下来遇到了Promisenew Promise立即执行,输出promisethen函数分发到当前宏任务的微任务

第三步,遇到console.log(),立即执行,输出console

第四步,执行当前宏任务的微任务,输出then

第五步,执行另一个宏任务,输出setTimeout

总结

了解调用堆栈事件循环Web API回调队列微任务队列对于编写高效且响应迅速的 JavaScript 代码至关重要。