簡體   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