如何掌握异步JavaScript?

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

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

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

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

一、异步JavaScript如何实现?

01. 使用回调

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

举个例子,代码如下:

js 复制代码
setTimeout(function() {
  console.log('executed after a 3 second delay.');
}, 3000)

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

02. 使用Promise

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

举个例子,代码如下:

js 复制代码
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
  }, 3000);
});

promise.then((result) => {
  console.log(result);
  // 输出:Success
});

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

03. 使用async/await

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

举个例子,代码如下:

js 复制代码
async function example() {
  const result = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Success!');
    }, 3000);
  });

  console.log(result);
  // 输出:Success
}

example();

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

二、异步JavaScript如何执行?

编写JavaScript异步代码时,了解JavaScript运行时如何处理和执行任务至关重要。这就需要你充分理解调用堆栈、事件循环、Web API、回调队列和微任务队列等概念。 你也许已经看出来了,这些概念其实说的就是要掌握和理解事件循环的执行机制。

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

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

01. 调用堆栈

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

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

图如下所示:

[入栈演示图]

[出栈演示图]

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

代码如下:

js 复制代码
function foo() {
  console.log('foo');
}

function bar() {
  console.log('bar');
  foo();
}

bar();

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

02. 事件循环

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

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

代码如下:

js 复制代码
console.log('start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

console.log('end');

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

03. Web API

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

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

04. 回调队列

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

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

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

代码如下:

js 复制代码
setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise');
});

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

05. 微任务队列

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

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

代码如下:

js 复制代码
Promise.resolve().then(() => {
  console.log('Promise');
});

console.log('end');

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

三、实例分解执行过程

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

什么是事件循环?

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

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

01. 先来看一张流程图

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

02. 再来看一个例子

js 复制代码
new Promise(function(resolve) {
  console.log('promise');
  resolve();
}).then(function() {
  console.log('then');
})

console.log('console');

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

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

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

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

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

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

总结

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

相关推荐
Myli_ing6 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风8 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave15 分钟前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟17 分钟前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾39 分钟前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧1 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7011 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
幼儿园的小霸王2 小时前
通过socket设置版本更新提示
前端·vue.js·webpack·typescript·前端框架·anti-design-vue