浅谈promise和async/await执行顺序

一、产生背景

下面来看一个最简单的例子,这个期约在超时之后会解决为一个值:

let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 7));

这个期约在 1000 毫秒之后解决为数值 7。如果程序中的其他代码要在这个值可用时访问它,则需要
写一个解决处理程序:

let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 7));
p.then((x) => console.log(x)); // 7

这其实是很不方便的,因为其他代码都必须塞到期约处理程序中。不过可以把处理程序定义为一个函数:

function handler(x) { console.log(x); }
let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 7));
p.then(handler); // 7

这个改进其实也不大。这是因为任何需要访问这个期约所产生值的代码,都需要以处理程序的形式
来接收这个值。也就是说,代码照样还是要放到处理程序里。 async/await 旨在解决利用异步结构组织代码的问题。为此, ECMAScript 对函数进行了扩展,
为其增加了两个新关键字: async 和 await。

二、期约和异步函数执行顺序

如果仅仅单纯的使用async关键字,程序运行结果和普通函数并没有什么不同,加了await关键字的之后,JavaScript 运行时在碰到 await 关键字时,会记录在哪里暂停执行。等到 await 右边的值可用了, JavaScript 运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。因此,即使 await 后面跟着一个立即可用的值,函数的其余部分也会被异步求值。
接下来用一个简单的例子演示这一点

async function bar(){
  console.log(1)
  await null
  console.log(2)
}
console.log(3)
bar()
console.log(4)
// 3 1 4 2

控制台很好的解释了运行时的工作过程

  1. 打印3
  2. 调用异步函数bar()
  3. 在bar()中打印1
  4. 在bar()中 await 关键字暂停执行,为立即可用的值null向消息队列添加一个任务
  5. bar()退出
  6. 打印4
  7. 同步线程执行完毕
  8. JavaScript运行时从消息队列中取出任务,恢复异步函数执行
  9. 在bar()中恢复执行,await取得null值(这里没有使用)
  10. 在bar()中打印2
  11. bar()返回

如果await后面是一个期约,则问题会复杂一些

async function foo() {
   console.log(2);
   console.log(await Promise.resolve(8));
   console.log(9);
}
async function bar() {
   console.log(4);
   console.log(await 6);
   console.log(7);
}
console.log(1);
foo();
console.log(3);
bar();
console.log(5);
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9

引用红宝书第四版
运行时会像这样执行上面的例子:
(1) 打印 1;
(2) 调用异步函数 foo();
(3)(在 foo()中)打印 2;
(4)(在 foo()中) await 关键字暂停执行,向消息队列中添加一个期约在落定之后执行的任务;
(5) 期约立即落定,把给 await 提供值的任务添加到消息队列;
(6) foo()退出;
(7) 打印 3;
(8) 调用异步函数 bar();
(9)(在 bar()中)打印 4;
(10)(在 bar()中) await 关键字暂停执行,为立即可用的值 6 向消息队列中添加一个任务;
(11) bar()退出;
(12) 打印 5;
(13) 顶级线程执行完毕;
(14) JavaScript 运行时从消息队列中取出解决 await 期约的处理程序,并将解决的值 8 提供给它;
(15) JavaScript 运行时向消息队列中添加一个恢复执行 foo()函数的任务;
(16) JavaScript 运行时从消息队列中取出恢复执行 bar()的任务及值 6;
(17)(在 bar()中)恢复执行, await 取得值 6;
(18)(在 bar()中)打印 6;
(19)(在 bar()中)打印 7;
(20) bar()返回;
(21) 异步任务完成, JavaScript 从消息队列中取出恢复执行 foo()的任务及值 8;
(22)(在 foo()中)打印 8;
(23)(在 foo()中)打印 9;
(24) foo()返回。
注释: TC39 对 await 后面是期约的情况如何处理做过一次修改。修改后,本例中的 Promise.resolve(8)只会生成一个
异步任务。因此在新版浏览器中,这个示例的输出结果为 123458967。实际开发中,对于并行的异步操作我们通常
更关注结果,而不依赖执行顺序。 ——译者注

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值