在上一篇文章中,有说过v8的async优化节约了一个额外的Promise和两个Microtask,那么本篇文章来更详细的讲述一下优化了哪两个Microtask。
还是这个例子:
async function foo() {
const w = await v;
return w;
}
在上篇文章中有上面这样的一个图,讲述了V8是如何处理这个await的,那么转换为js代码是怎样的呢?
function foo(v) {
const implicit_promise = new Promise((resolve, reject) =>{
const promise = Promise.resolve().then(return v);
const throwaway = new Promise((resolve, reject) => {
promise.then(resolve, reject);
}).then(result => {
resumeFoo(result);
}).catch(err => {
throwFoo(err);
});
const resumeFoo = function (result) {
const returnValue = (v => {
// 原来的foo执行逻辑
const w = v;
return w;
})(result);
resolve(returnValue);
};
const throwFoo = err => {
reject(err);
}
});
return implicit_promise;
}
在js执行时,会把每个promise的then塞入Microtasks,而Microtasks会在每次事件循环后进行处理,于是参照上面的js代码可以看到:
第一个Microtask:
const promise = Promise.resolve().then(return v);
这个Microtask中无论v是不是一个Promise,通过Promise.resolve().then
这样的处理之后都会变为一个Promise,但是这对于v本来就是一个Promise的话显得很多余,于是在v8中判断了下如果是Promise就不再包裹,减少了一次Microtasks。
第二个Microtask:
promise.then(resolve, reject);
第三个Microtask:
const throwaway = new Promise((resolve, reject) => {
promise.then(resolve, reject);
}).then(result => {
resumeFoo(result);
}).catch(err => {
throwFoo(err);
});
能够看到throwaway这部分代码完全是可以优化成下面这样的:
promise.then(resumeFoo, throwFoo);
又减少了一个Microtasks,于是最终的代码变成了这样:
function foo() {
const implicit_promise = new Promise((resolve, reject) =>{
v.then(resumeFoo, throwFoo);
const resumeFoo = function (result) {
const returnValue = (v => {
// 原来的foo执行逻辑
const w = v;
return w;
})(result);
resolve(returnValue);
};
const throwFoo = err => {
reject(err);
}
});
return implicit_promise;
}
那么再来看之前的这段代码的输出:
const p = Promise.resolve();
(async () => {
await p; console.log('after:await');
})();
p.then(() => console.log('tick:a'))
.then(() => console.log('tick:b'));
变换成node12的执行逻辑:
const p = Promise.resolve();
(async () => {
const implicit_promise = new Promise((resolve, reject) =>{
v.then(resumeFoo, throwFoo);
const resumeFoo = function (result) {
const returnValue = (v => {
console.log('after:await');
})(result);
resolve(returnValue);
};
const throwFoo = err => {
reject(err);
}
});
return implicit_promise;
})();
p.then(() => console.log('tick:a'))
.then(() => console.log('tick:b'));
所以按照从上到下执行的逻辑就是:
1.先触发第一个then,把 resumeFoo 塞入microtask2.触发第二个then,把 console.log('tick:a')
塞入microtask
开始执行Microtasks,输出 after:await
,再输出 tick:a
。
然后再触发第三个then将 console.log('tick:b')
塞入Microtasks,再去清理Microtasks,执行输出 tick:b
。
文章评论