[英]Why does an async function finishes executing after a promise started later?
这是关于事件循环的事情,我不明白。
这是代码:
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2 start'); return new Promise((resolve, reject) => { resolve(); console.log('async2 promise'); }) } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }).then(function() { console.log('promise3'); }); console.log('script end');
结果是:
script start
async1 start
async2 start
async2 promise
promise1
script end
promise2
promise3
async1 end
setTimeout
我不明白为什么在promise 2
和promise 3
之后打印async1 end
。
在解释这一点的事件循环中会发生什么? 这些微任务在队列中按什么顺序推送和弹出?
你很惊讶为什么async1 end
出现在promise2
和promise3
之后,尽管它是在它们之前调用的,并且微任务是按照它们入队的顺序执行的。
但是,它实际上归结为async
function 需要多少微任务才能解决。
看看这个(它是相同的代码,但有 4 个原始承诺):
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2 start'); return new Promise((resolve, reject) => { resolve(); console.log('async2 promise'); }) } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }).then(function() { console.log('promise3'); }).then(function() { console.log('promise4'); }); console.log('script end');
/* Just to make the console fill the available space */.as-console-wrapper { max-height: 100%;important; }
哎呀, async1 end
不再是最后:它出现在promise4
之前!
那么这告诉我们什么呢? 这意味着async1 end
在 3 个微任务之后被记录(不包括由promiseN
引起的那些)。
什么需要这 3 个微任务? 让我们检查一下:
最后一个很明显: async1
中的await
运算符消耗了一个。
我们还剩两个。
To see that, in async2
, instead of creating a promise through the Promise
constructor, create a thenable (an object with a .then .then()
method, aka. promise-like object), that acts the same (well, a real promise is复杂得多,但为了这个例子,它是这样工作的)。 我看起来像这样:
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2 start'); console.log('async2 promise'); return { then(resolve, reject){ queueMicrotask(() => { resolve(); console.log('async2 resolve'); }); return Promise.resolve() } }; } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }).then(function() { console.log('promise3'); }).then(function() { console.log('promise4'); }); console.log('script end');
/* Just to make the console fill the available space */.as-console-wrapper { max-height: 100%;important; }
但是,您可能会发现仍然有问题。 promise2
在async2 resolve
之前仍然被调用。
async
函数在到达return
语句之前返回 promise。 这是有道理的,但这也意味着,它们不能返回通过return
的相同promise object 。 他们也必须等待返回的 promise!
那么,让我们看看我们的自定义then
何时调用:
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2 start'); console.log('async2 promise'); return { then(resolve, reject){ console.log('async2 then awaited') queueMicrotask(() => { resolve(); console.log('async2 resolve'); }); } }; } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }).then(function() { console.log('promise3'); }).then(function() { console.log('promise4'); }); console.log('script end');
/* Just to make the console fill the available space */.as-console-wrapper { max-height: 100%;important; }
啊哈,在一个新的微任务中!
我们找到了所有的漏洞,所以现在我们可以看到async
事物是如何与promise
交错执行的(这里-->
表示enqueues , <--
表示日志;微任务用μt
标记):
MACROTASK #0
<-- script start
setTimeout enqueued, but it creates a MACROTASK, so it always comes at last --> MT#1
async1 called
<-- async1 start
async2 called
<-- async2 start
promise executor called synchronously
<-- async2 promise
resolved promise returned to async2
async2 execution halted --> μt#1
async1 execution halted at await
promise executor called synchronously
<-- promise1
promise1 resolved --> μt#2
`then` chain built
<-- script end
microtask #1
async2 continues, calls `then` of the returned promise
<-- async2 `then` awaited
promise's `then` enqueues microtask for calling callback of async2 --> μt#3
microtask #2
promise2 `then` called
<-- promise2
promise2 resolved --> μt#4
microtask #3
called queued callback of promise
<-- async2 resolve
async2 completes
promise returned by async2 resolves --> μt#5
microtask #4
promise3 `then` called
<-- promise3
promise3 resolved --> μt#6
microtask #5
async1 continues
<-- async1 end
async1 completes
microtask #6
promise4 `then` called
<-- promise4
promise4 resolved
MACROTASK #1
timer callback called
<-- setTimeout
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.