参考链接:
async、await 实现原理
async 和 await
1.异步编程回顾
由于 JavaScript 是单线程执行模型,因此必须支持异步编程才能提高运行效率。异步编程的语法目标是让异步过程写起来像同步过程。
异步编程的发展经历了: 回调函数→Promise→ES7中的async/await
Promise函数在这里不赘述,会另外写一篇Promise的总结,所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
2.async/await
实际上,async/await就是Generator + Promise的语法糖,await必须在async内使用,并装饰一个Promise对象,async返回的也是一个Promise对象。
async 是异步的意思,await 则可以理解为等待。放到一起可以理解async就是用来声明一个异步方法,而 await 是用来等待异步方法执行。
async
async
声明的函数返回一个 Promise 对象,它可以包含多个 await
表达式,这些表达式后面通常跟随着 Promise 对象。当遇到 await
表达式时,async
函数会暂停执行,等待 Promise 对象的状态改变(即 resolve
或 reject
),然后恢复执行,并将 resolve 的结果作为 await
表达式的值返回。
所以,对于async关键字,以下两种方法是等效的:
await
而对于await函数,正常情况下,await命令后面是一个Promise对象,返回该对象的结果。如果不是Promise对象,就直接返回对应的值。
在这段代码中,先定义了一个异步函数f,它会返回一个立即resolved的Promise对象,其resolved值为123。然后,通过调用f函数,并使用then方法注册回调函数的方式来获取函数返回的Promise对象。
在异步函数f中:
async function
关键字声明了这是一个异步函数,它会返回 Promise 对象,而且在函数中可以使用await
表达式。return await 123
表示等待 Promise 对象123
的状态改变(即立即被 resolve),然后将其 resolve 的结果作为函数返回值返回。由于123
不是一个 Promise 对象,会自动被包装成一个 resolved 状态的 Promise 对象返回,因此其实等价于直接返回123
。
因此,这个异步函数 fn1
的返回值实际上就是 Promise.resolve(123)
,也就是一个立即 resolve 的 Promise 对象,其 resolved 的值为 123
。
然后这句代码:
f().then(v => console.log(v))
- 调用
fn1()
函数,返回一个 Promise 对象。 - 使用
then
方法在该对象上注册回调函数,该回调函数的参数v
就是 Promise 对象 resolve 的值。 - 在回调函数中,调用
console.log(v)
打印该值。
示例分析
例1
async/await函数的执行顺序:
遇到await时,执行await后面的内容,但是会阻塞下面的代码(即加入微任务队列),先执行 async 外面的同步任务,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码。
例2:使用 async/await 结合 setTimeout 实现每隔1s 打印一个等于号的功能
async function printEqual() {while(true) {await new Promise((resolve) => setTimeout(resolve, 1000)) // 等待 1sconsole.log('=')}
}printEqual()
当我们使用 await
关键字等待一个异步函数时,该异步函数必须返回一个 Promise
对象。在这种情况下,await
表达式会暂停当前函数的执行,等待 Promise
对象状态变为 resolved 后恢复执行。同时,由于异步函数本身是不会被阻塞的,因此可以避免阻塞主线程。
在本例中,使用 await
等待了一个 Promise
对象,该 Promise
对象使用 setTimeout
方法创建,实现了一定时间后返回一个 resolved 状态的 Promise
,然后通过调用 resolve
函数来设置 Promise 的状态并返回结果。因为 setTimeout
会在一定时间后触发传入的回调函数,所以使用 setTimeout(resolve, 1000)
意味着将在 1 秒后出发 resolve 函数,从而返回一个 resolved 的 Promise 对象。在使用 await
后,程序将会等待 1 秒的时间,然后继续执行 await 之后的代码。
综上所述,await new Promise((resolve) => setTimeout(resolve, 1000))
表示等待 1 秒钟之后再执行下一步操作,由于是异步的,因此不会阻塞主线程。