[英]Correct mental model for a Javascript async function's 'await': generator's 'yield' vs. 'promise.then()'?
生成器的產量與promise.then()中的哪個是更*正確的心理模型,可以理解“等待”?
屬性比較,通過使用調試器逐步瀏覽以下代碼段來推斷:
等待:
await不會暫停/暫停正在運行的異步函數的執行。 (正在運行的異步函數“運行至完成”,在解釋器達到第一個等待狀態時返回未決的諾言。然后立即將其從調用堆棧中刪除。)
等待等待諾言實現。
await expression
將函數的其余代碼包裝在微任務中。
發電機收益:
yield promise
確實可以確保在執行剩余代碼之前已經promise
。 promise.then(回調):
//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()
來自外部的方法來運行代碼yield
的yield
。 另一方面,異步函數會創建一個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.