繁体   English   中英

为什么在稍后启动 promise 之后异步 function 完成执行?

[英]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 2promise 3之后打印async1 end

在解释这一点的事件循环中会发生什么? 这些微任务在队列中按什么顺序推送和弹出?

你很惊讶为什么async1 end出现在promise2promise3之后,尽管它是在它们之前调用的,并且微任务是按照它们入队的顺序执行的。

但是,它实际上归结为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; }

但是,您可能会发现仍然有问题。 promise2async2 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM