v8是如何实现更快的await

发布时间:2022-03-25 09:51:30 作者:iii
来源:亿速云 阅读:169

本篇内容介绍了“v8是如何实现更快的await ”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Example

首先看下代码:

const p = Promise.resolve();

(async () => {

  await p;

  console.log("after:await");

})();

p.then(() => {

  console.log("tick:a");

}).then(() => {

  console.log("tick:b");

});

以 node v10 的执行结果为准,node v8 的实现是不符合ECMAScript 标准

优秀的程序员总是能以简单的例子解释复杂的原理。代码很简单,但是执行结果可能出乎很多人意料:

tick:a

tick:b

after:await

如果你已经猜对了,本文的关键内容你已经掌握,不用往下看了:)。

为什么 after:await会出现在tick:a之后,甚至是tick:b之后? 要理解其中的原理,我们可以做一个小实验。

将 await 翻译成 promise

v8博客中是以伪代码的方式解释await的执行逻辑:

原图 https://v8.dev/_img/fast-async/await-under-the-hood.svg

我们可以用promise语法写成:

function foo2(v) {

  const implicit_promise = new Promise(resolve => {

    const promise = new Promise(res => res(v));

    promise.then(w => resolve(w));

  });

  return implicit_promise;

}

按照同样的方式,可以将文章开头的代码转换成:

const p = Promise.resolve();

(() => {

  const implicit_promise = new Promise(resolve => {

    const promise = new Promise(res => res(p));

    promise.then(() => {

      console.log("after:await");

      resolve();

    });

  });

  return implicit_promise;

})();

p.then(() => {

  console.log("tick:a");

}).then(() => {

  console.log("tick:b");

});

经过一些琐碎的调试,发现问题真正的关键代码是这一句: const promise = new Promise(res => res(p));

Resolved with another promise

了解 Node.js 或浏览器的事件循环的童鞋都知道,resolved promise 的回调函数(reaction)是放在一个单独的队列MicroTask Queue中。 这个队列会在事件循环的阶段结束的时候被执行,只有当这个队列被清空后,才能进入事件循环的下一个阶段。

我们知道一个 promise 的 .then 回调的返回值可以是一个任意值,也可以是另外一个 promise。 但是后者的处理逻辑可能有点反直觉。

在深入之前,我们简单说一下 promise 的几种状态:

我们说一个 promise 是 resolved 的,表示它不能被再次 fulfill 或 reject, 要么是被 fulfill,要么被 reject(这两种情况,promise 均有一个确定的 non-promise result), 要么遵循另外一个 promise(随之 fulfill 或 reject)

我们说一个 promise 是 unresolved 的,表示它尚未被 resolve

当一个 promise(假设叫 promiseA。方便引用) 被 resolve,并且去遵循另外一个 promise(叫 p) 时,执行逻辑和前面两种 resolve 情况非常不同,用伪代码表示则是:

addToMicroTaskQueue(() => { // 任务A

  // 使用 .then 方法,将 promiseA 的状态 和 p 绑定

  p.then(

    resolvePromiseA, // 任务B

    rejectPromiseA

  );

});

我们一步一步来分析:

首先,我们在MicroTask Queue添加任务A,该任务在 ECMAScript 标准 中被定义为 PromiseResolveThenableJob

任务A,主要目的是使 promiseA 遵循 p 的状态,将两者的状态关联起来。

由于我们例子中 p 已经是 resolved(状态为fulfilled)的,所以立即将resolvePromiseA任务B 添加到MicroTask Queue中

在 resolvePromiseA 执行后,promiseA 才是 resolved (状态为 fulfilled,值为 p 的 fulfilled value)

我们可以看到,从 new Promise(res=>res(p)) 到该调用返回的 promise 真正被 resolve 至少需要两次microtick——在我们的例子中,是遍历了两次 MicroTask Queue

这个时候,我们终于可以理清楚开头代码的执行顺序:

01月28日更新,之前微任务队列里面的任务没有考虑顺序,这里做一下修改,以下队列里的任务是顺序有关,从左往右,左边的先执行

1、当代码执行完后

MicroTask Queue有两个任务:PromiseResolveThenableJob,tick:a

2、开始执行 runMicrotasks()

MicroTask Queue变成:resolvePromiseA,tick:b

console: tick:a

3、MicroTask Queue没有清空,继续执行队列中的任务

MicroTask Queue变成:after:await

console: tick:a, tick:b

4、继续执行,清空MicroTaak Queue

console: tick:a, tick:b, after:await

“v8是如何实现更快的await ”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

推荐阅读:
  1. 如何使自己的网页打开更快?
  2. async/await的应用

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

await

上一篇:JavaScript有哪些常见的比较运算符

下一篇:JavaScript常见逻辑运算符有哪些

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》