我開始學習異步 Javascript,我真的很困惑。
老實說,async/await 方法對我來說似乎很合乎邏輯。我們需要讓運行時知道我們正在執行異步操作,以便它可以相應地處理它。但是為什么我們在使用 .then() 方法時不需要做同樣的事情呢?我的意思是,如果 Javascript 已經能夠理解何時處理承諾,那么不能像 .then() 一樣在沒有異步的情況下使用 await 嗎?
更令人困惑的是,我看到有人在用 async 關鍵字宣告的函式中直接使用 .then() 。async/await 不應該是 .then().catch() 方法的語法糖嗎?為什么可以將這些組合在一起,尤其是在彼此之間?在 async 函式的結果上使用 .then() 不會那么令人困惑,但是彼此之間的相處讓我更難理解這一點。
我真的到處搜索有關此問題的解釋,但找不到這個確切問題的答案。我所發現的只是人們說你可以同時使用這兩種方法,因為它們本質上是一樣的,但是當你了解細節時,事情就不是很清楚了。
所以 async 函式總是回傳一個 promise。在里面, await 總是處理承諾。.then() 可以鏈接到 await 函式。.then() 也可以鏈接到異步函式的結果。如果我們不想在 await 上使用 try/catch,則與 .catch 方法相同。為什么會如此混亂?我們可以在沒有 .then() 的情況下處理異步的回傳嗎?如果 async/await 真的是 .then() 的語法糖,那么為什么 .then() 在它決議后也不總是回傳一個 promise?
如果有人可以幫助澄清一些問題,我將不勝感激。謝謝!
uj5u.com熱心網友回復:
這里有很多問題,但一種思考方式是…… Promise 不是異步操作,它們只是一種標準模式,可以幫助用戶以可預測的方式處理現有的異步操作。
沒什么特別的.then()。它不是 Javascript 關鍵字,并且 javascript 不需要以任何方式“知道” then。
承諾是一種模式。您可以自己從頭開始撰寫一個 Promise 類(我強烈推薦這樣做)。當您使用.then()并傳遞一個函式時,您是在告訴 Promise 類:“當 Promise 解決時,請為我呼叫此函式”。
所有這些都存在于 async/await 之前。之后添加了 Async/await 以使其更容易使用 Promise。
我發現“某些東西是語法糖”這個問題毫無意義。它表明語言特征沒有那么有意義,因為在語言特征存在之前可以完成同樣的事情。雖然這可能是真的,但與匯編語言相比,任何編程語言都是如此。對我來說,“語法糖”的定義幾乎可以擴展到任何東西,所以它不是一個有用的名稱。
什么 async/await(以及它之前的生成器)添加到語言中,是一個 javascript 函式可以在某些條件下被中斷和恢復。
最后,.then()和.catch()總是回傳承諾。如果您看到一個.then()不兼容的函式,那么它與 Promise/A 規范不兼容。
如果您愿意投入作業以完全理解這一點,我會推薦以下兩個練習:
- 撰寫自己的 Promise 類
- 使用生成器函式重新實作 async/await(請參閱
coasync/await 登陸之前如何完成的包)
uj5u.com熱心網友回復:
async/的目的await是允許以串行方式撰寫異步代碼,這在精神上更容易推理(對于某些人而言)。如果您需要在繼續執行其余代碼之前等待異步操作完成,這將非常有用。例如,如果您需要將異步操作的結果作為引數傳遞。
示例 1
function asyncOperation1(n) { return Promise.resolve(n 1); }
function asyncOperation2(n) { return Promise.resolve(n/2); }
function asyncOperation3(n) { return Promise.resolve(n*3); }
function errorHandler(err) { console.error(err); }
function main() {
// flow-control
asyncOperation1(1)
.then(asyncOperation2)
.then(asyncOperation3)
.then(continueAfterAsync)
.catch(errorHandler)
// function wrapper
function continueAfterAsync(result) {
console.log(result);
}
}
main();
使用async/上面函式await的代碼main可能看起來像
async main() {
try {
console.log(
await asyncOperation3(
await asyncOperation2(
await asyncOperation1(1)
)
)
);
} catch(err) {
errorHandler(err);
}
}
注意我們不需要重寫異步操作函式async function asyncOperation...來使用await,但是我們需要宣告main函式為async main。
哪個更好(?)取決于開發人員的品味和以前的編程語言經驗。我可以看到的好處是您不需要將所有內容都包裝到函式中并引入額外的流控制代碼,將這種復雜性留給 JavaScript 編譯器。
但是,在某些情況下,當您想要安排一些并行任務而您并不關心哪個先完成時。僅使用async/來處理此類事情相對困難await。
示例 2
function main() {
Promise
.all(
['srv1', 'srv2', 'srv3'].map(
srv => fetch(`${srv}.test.com/status`)
)
])
.then(
responses => responses.some(res => res.status !== 200) ?
console.error('some servers have problems') :
console.log('everything is fine')
)
.catch(err => console.error('some servers are not reachable', err))
}
因此,我們看到兩者都存在.then()并await可以共存的空間。
在某些情況下,函式可能是同步的,也可能是異步的,這取決于業務邏輯(我知道這很丑陋,但在某些情況下是不可避免的)。我們來到你的主要問題
why don't we need to mark an asynchronous operation with .then() and we have to do it with await
In other words, why do we need async keyword at all?
Example 3
// without `async`
function checkStatus(srv) {
if (!srv.startsWith('srv')) {
throw new Error('An argument passed to checkStatus should start with "srv"')
}
return fetch(`https://${srv}.test.com/status`);
}
function main() {
// this code will print message
checkStatus('srv1')
.then(res => console.log(`Status is ${res.status === 200 ? 'ok': 'error'}`))
.catch(err => console.error(err));
// this code will fail with
// Uncaught TypeError: (intermediate value).then is not a function
checkStatus('svr1')
.then(res => console.log(`Status is ${res.status === 200 ? 'ok': 'error'}`))
.catch(err => console.error(err));
}
However, if we define async function checkStatus, compiler will wrap the runtime error into rejected promise return value, and both parts of the main function will work.
Now let's imagine that JavaScript allows to write functions that use await without specifying async in front of them.
Example 4 (not a valid Javascript)
function checkStatus(srv) {
if (cache[srv]) {
data = cache[srv];
} else {
data = (await fetch(`https://${srv}.test.com/status`)).json();
}
data.x.y = 'y';
return data;
}
What would you expect checkStatus to return? Promise, raw value or throw exception (in case data.x is undefined)?
If you say Promise, then it would be hard for developer that uses this function to understand why inside of checkStatus one can write data.x and outside of it (await data).x is required.
If raw value, the whole execution flow becomes cumbersome, and you can no longer rely on the fact that JavaScript is a single-threaded language, where no-one can change the value of the variable between two lines of code that are written in serial manner.
As you noticed, async/await is a syntactic sugar. If this syntax allows me to avoid possible runtime errors at earlier stage and keep the language backward compatible, I'm eager to pay the price of putting extra async in front of async functions.
Also, I would recommend to read the answers to JS async/await - why does await need async?
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/344611.html
標籤:javascript 节点.js 异步 异步等待 es6-promise
上一篇:為什么我們需要等待?
