这是一篇简单的短文章, 方便理解.
开局先丢官宣: 这个链接是对 await 的解释, 解释了它的执行.
await 的执行意味着(官宣巴拉巴拉地说了 14 点, 这里简化成 2 点):
1. await 以 promise 形式完成, 且 await 之后的代码在 promise 被完成后以 .then 执行.
2. 遇到 await 时, 将 async 上下文 从上下文堆栈中移除, 将上级 (async 之外) 上下文 (依次) 恢复为当前执行的上下文; 这步过程中需设置 async 上下文的评估状态, 以便在恢复到执行上下文时调用 await 之后的步骤.
第 2 点类似的理解为 async 上下文 被挂起, 依次向上运行上下文堆栈中的上下文代码, 完了后执行 async 函数里 await 之后的代码.
估计看的绕, 用代码说话吧.
例一
- console.log(1);
- async function async1(){
- console.log(2);
- await async2();
- console.log(3);
- };
- async function async2(){ console.log(4)};
- async1();
- console.log(5);
- // 输出结果 1 2 4 5 3
理解一下输出结果:
第一步: 输出 1;
第二步: 输出 2,async1 先被调用, 肯定优先于函数内的 console 和后面的 console.log(5);
第三步: 输出 4, 把 await async2() 转为 promise 形式:
- new Promise(resolve => {
- console.log('2')
- resolve();
- }).then(res => {
- // 抽出 await 之后的代码放到. then
- console.log(3);
- });
这时候先输出 2, 再等 3 的输出.
但由于 3 在 Promise 规范里被设定为异步(划重点: ES6 中属 microTask , 此处直接影响低版本中对 promise 实现的 shim ), 且 await 表示遇到 await 关键字后先将这块代码的 asyncContext 挂起并执行上级上下文, 所以先执行了 5, 于是...
第四步: 输出 5, 执行完后回到 async context, 再于是...
第五步: 最后输出 3.
例二
在官宣上没看到 依次向上字样鸭, 咋肥事? 继续代码:
- console.log(1);
- function fn1(){
- function fn2(){
- async function async1(){
- console.log(2);
- await fn3();
- console.log(3);
- }
- function fn3(){ console.log(4); }
- async1();
- new Promise(resolve => resolve(5)).then(res => console.log(res));
- console.log(6);
- }
- fn2();
- console.log(7);
- }
- fn1();
- console.log(8);
- // 输出结果 1 2 4 6 7 8 3 5
理解一下输出结果:
第一步: 输出 1;
第二步: fn1 被执行, fn2 被执行, 输出 2;
第三步: fn3 被执行, 如第一例所述, 输出 4;
第四步: async context 被挂起, 将 fn2 上下文作为运行上下文, 输出 6;
第五步: fn2 上下文处理后继续向外更新, fn1 上下文作为运行上下文, 输出 7;
第六步: 重复上述, 输出 8;
第七步: 由于 fn3 的 await(promise)在 fn2 中的 new Promise 前被加入执行列队, 根据先进先出的执行顺序, 输出 3;
第八步: 最后输出 5.
例三
如果 2 个 async 嵌套顺序是啥样的呢? 再看代码:
- console.log(1);
- function fn1(){
- async function fn2(){
- async function async1(){
- console.log(2);
- await fn3();
- console.log(3);
- }
- function fn3(){ console.log(4); }
- await async1();
- new Promise(resolve => resolve(5)).then(res => console.log(res));
- console.log(6);
- }
- fn2();
- console.log(7);
- }
- fn1();
- console.log(8);
- // 1 2 4 7 8 3 6 5
重复上一个理解的过程, 把 await async1(); 后的代码放到最后执行, 看做:
- new Promise(resolve => {
- // async1 里的代码
- resolve();
- }).then(res => {
- new Promise(resolve => resolve(5)).then(res => console.log(res));
- console.log(6);
- });
对比以上输出结果, 正确!
如果有多个或者嵌套 promise, 则以 await 变成 promise 并挂起 async 上下文等上级上下文执行完后恢复 和 事件的执行顺序遵循先进先出 两个规则去完成推断执行顺序.
注意
1. 在低版本浏览器中, async/await 也有存在一些执行顺序的偏差(或者根本就不支持);
2. 在 ES6 和 ES5 中 promise 的执行也有不同点(上述提到, ES6 中 promise 属 microtask; 在 ES5 中, 暂未接触到有 API 直接操作 microtask 的, 所以. then 的异步是用 setTimeout 代替, 属 macrotask, 导致输出有差异); 关于 promise 也可参考上文 Promise 拆分理解和组装
3. 由于客户端环境不可控性太高, 建议用于 Node.JS 端开发.
彩蛋
通过上面的理解, 再看下面的图片(这是 koa middleware 的... 嘿嘿嘿):
希望这篇笔记能够帮助前端袍泽们对 async await 的执行过程有更好的理解. 上述内容仅供参考, 如有理解不当的地方, 还望提出!
来源: https://www.cnblogs.com/ys-ys/p/9993335.html