簡體   English   中英

Javascript異步函數的“等待”的正確心智模型:生成器的“產量”與“ promise.then()”?

[英]Correct mental model for a Javascript async function's 'await': generator's 'yield' vs. 'promise.then()'?

生成器的產量與promise.then()中的哪個是更*正確的心理模型,可以理解“等待”?

屬性比較,通過使用調試器逐步瀏覽以下代碼段來推斷:

等待:

  1. await不會暫停/暫停正在運行的異步函數的執行。 (正在運行的異步函數“運行至完成”,在解釋器達到第一個等待狀態時返回未決的諾言。然后立即將其從調用堆棧中刪除。)

  2. 等待等待諾言實現。

  3. await expression將函數的其余代碼包裝在微任務中。

發電機收益:

  1. yield會暫停正在運行的函數的執行。 生成器功能不會“運行完成”。
  2. yield promise確實可以確保在執行剩余代碼之前已經promise
  3. yield不會包裝或創建微任務。

promise.then(回調):

  1. 不會暫停正在運行的函數的執行。
  2. 在執行回調之前,等待promise成立。
  3. 創建一個微任務(回調)

 //promise returning function function foo(whoCalled) { let p = new Promise(function(resolve, reject) { setTimeout( () => { console.log('resolving from setTimeout - called by: ' + whoCalled) resolve('resolve value') }, .1) }) return p } //async await async function asyncFunc() { await foo('async function') //rest of running function's code… console.log('async function howdy') } //generator yield: function* gen() { yield foo('generator function') //rest of running function's code… console.log('generator function howdy') } //promise.then(): function thenFunc() { let r = foo('promise.then function').then(() => { //rest of running function's code… console.log('promise.then() howdy') }) return r } //main function main() { //async await var a = asyncFunc() console.log(a) //logs Promise { <pending> } //the rest of the code following await foo() runs as a microtask runs once foo() resolves. The call stack was cleared. //generator var g = gen() console.log(g) // logs Object [Generator] {} var p = g.next().value console.log(p) //logs Promise { <pending> } g.next() //the rest of the code following yield running gen function's code runs. call stack was not cleared. //promise.then() var x = thenFunc() console.log(x) //logs Promise { <pending> } //the then(callback) microtask runs once foo() resolves. The call stack was cleared } main() console.log('main is off the call stack - launch/startup macrotask completing. Event loop entering timer phase.') 

而且,超越這種比較,究竟是什么樣的精確思維模型await引擎蓋下呢?

等待最新的ECMAScript規范以供參考: https : //www.ecma-international.org/ecma-262/10.0/index.html#await

在V8源代碼中等待: https : //github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/builtins/builtins-builtins-async-function-gen.cc#L252

這不是一個。 實際上,它們都是在一起: async / await = yield + then +一個跑步者。

async function的確會被await關鍵字掛起,就像生成器function*會被yield關鍵字掛起一樣。 在控制流語句中間停止和恢復執行的機制完全相同。

不同之處在於這些延續的驅動方式以及函數返回的內容。 發電機函數創建一個名為當發電機對象,你必須明確地調用next()來自外部的方法來運行代碼yieldyield 另一方面,異步函數會創建一個Promise,並自行管理執行。 它不等待外部next()調用,而是盡快運行每個異步步驟。 它不會將這些next()調用返回的結果值返回Promise.resolve()到promise,然后調用then方法將繼續作為回調傳遞。 當到達return ,它不會向調用方發出“迭代結束”的信號,而是使用返回值來解析最初返回的promise。

承諾和收益並不是最容易掌握的,尤其是當您不知道它們如何在幕后工作時。 因此,讓我們從基礎開始。 首先要了解的是Javascript是單線程的,這意味着它只能同時執行一項操作。 您仍然可以“一次”執行多個操作的方式是因為javascript有一個稱為事件循環的事件。

事件循環基本上看起來像這樣:

while(queue.waitForTasks()) {
   queue.performNextTask();
}

事件循環的作用是檢查是否有新的“任務”要運行Javascript。 如果有任務。 然后它會執行,直到沒有其他任務可以執行為止。 並且它將等待其新任務。 這些任務存儲在稱為隊列的內容中。

承諾,異步/等待

現在我們了解了Javascript如何處理不同的任務。 它如何與promise和async / await一起使用? promise無非是一項任務,或者在使用Javascript的情況下,某項保留了一項任務,它將被添加到隊列中,並在所有任務執行完畢后立即執行。 .then()是一種為您的promise提供回調的方法,一旦您的resolve回調被調用,該promise將被執行。

await [something]關鍵字告訴Javascript,嘿,把下一個[something]放在隊列的末尾,一旦[something]有結果要給我,請立即聯系我。

具有async關鍵字的函數基本上是在告訴Javascript:“此函數是一個承諾,但要立即執行”。

異步函數的流程最容易通過兩個不同的異步函數A和B來掌握/演示,如下所示:

const A = async () => {
    console.log(A: Start);
    for (var i = 0; i < 3; i++) {
        await (async () => console.log('A: ' + i));
    }
    console.log('A: Done');
}
const B = async () {
    console.log(B: Start);
    for (var i = 0; i < 3; i++) {
        await (async () => console.log('B: ' + i));
        await (async () => {/* A task without output */});
    }
    console.log('B: Done');
}

像這樣在等待時調用函數:

console.log('Executing A');
await A();
console.log('Executing B');
await B();

它會導致:

Executing A
A: Start
A: 0
A: 1
A: 2
A: Done
Executing B
B: Start
B: 0
B: 1
B: 2
B: Done

並運行:

console.log('Executing A');
A();
console.log('Executing B');
B();

會導致:

Executing A
A: Start       Note: still gets ran before Executing B
Executing B
B: Start
A: 0
B: 0
A: 1
A: 2           Note: A: 2 first because another task in B was put in the queue
A: Done
B: 1
B: 2
B: Done

了解這一點可能有助於更好地了解您的應用程序流程。

yield關鍵字類似於await ,在某種意義上,“外部力”控制何時繼續執行功能。 在這種情況下,不是完成promise任務,而是generator.next()函數

盡管我真的很想知道,但我不知道正確的心理模型的答案。

但我發現這很有趣

凱爾·辛普森(Kyle Simpson)的《您不知道JS》一書深入探討了如何在r / Javascript reddit上進行等待- 來源

“這是完全不正確的。生成器不會運行到完成狀態,大多數異步await的引擎實現實際上將它們像生成器一樣對待。遇到產量時,生成器會在本地暫停……字面上。Await使用相同的方法“。

“不,這都是不正確的廢話。大多數引擎將async-await視為生成器,它肯定會以屈服的方式進行局部停頓。 將promise.then()包裹在后續代碼中將是實現await的最幼稚且效率最低的方法之一即使發動機做到了(大多數沒有),這並不意味着這是正確的心理。模型。就像產量地方暫停是正確的心智模型“。

但是,當我親自查看ECMA腳本規范並使用vscode nodejs調試器瀏覽代碼時,await似乎更類似於.then()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM