摘要:因?yàn)楹瘮?shù)返回一個對象,所以可以用于等待一個函數(shù)的返回值這也可以說是在等函數(shù),但要清楚,它等的實(shí)際是一個返回值。幫我們干了啥作個簡單的比較上面已經(jīng)說明了會將其后的函數(shù)函數(shù)表達(dá)式或的返回值封裝成一個對象,而會等待這個完成,并將其的結(jié)果返回出來。
隨著 Node 7 的發(fā)布,越來越多的人開始研究據(jù)說是異步編程終級解決方案的 async/await。我第一次看到這組關(guān)鍵字并不是在 JavaScript 語言里,而是在 c# 5.0 的語法中。C# 的 async/await 需要在 .NET Framework 4.5 以上的版本中使用,因此我還很悲傷了一陣——為了要兼容 XP 系統(tǒng),我們開發(fā)的軟件不能使用高于 4.0 版本的 .NET Framework。
我之前在《閑談異步調(diào)用“扁平”化》 中就談到了這個問題。無論是在 C# 還是 JavaScript 中,async/await 都是非常棒的特性,它們也都是非常甜的語法糖。C# 的 async/await 實(shí)現(xiàn)離不開 Task 或 Task
現(xiàn)在拋開 C# 和 .NET Framework,專心研究下 JavaScript 的 async/await。
async 和 await 在干什么任意一個名稱都是有意義的,先從字面意思來理解。async 是“異步”的簡寫,而 await 可以認(rèn)為是 async wait 的簡寫。所以應(yīng)該很好理解 async 用于申明一個 function 是異步的,而 await 用于等待一個異步方法執(zhí)行完成。
另外還有一個很有意思的語法規(guī)定,await 只能出現(xiàn)在 async 函數(shù)中。然后細(xì)心的朋友會產(chǎn)生一個疑問,如果 await 只能出現(xiàn)在 async 函數(shù)中,那這個 async 函數(shù)應(yīng)該怎么調(diào)用?
如果需要通過 await 來調(diào)用一個 async 函數(shù),那這個調(diào)用的外面必須得再包一個 async 函數(shù),然后……進(jìn)入死循環(huán),永無出頭之日……
如果 async 函數(shù)不需要 await 來調(diào)用,那 async 到底起個啥作用?
async 起什么作用這個問題的關(guān)鍵在于,async 函數(shù)是怎么處理它的返回值的!
我們當(dāng)然希望它能直接通過 return 語句返回我們想要的值,但是如果真是這樣,似乎就沒 await 什么事了。所以,寫段代碼來試試,看它到底會返回什么:
async function testAsync() { return "hello async"; } const result = testAsync(); console.log(result);
看到輸出就恍然大悟了——輸出的是一個 Promise 對象。
c:var est> node --harmony_async_await . Promise { "hello async" }
所以,async 函數(shù)返回的是一個 Promise 對象。從文檔中也可以得到這個信息。async 函數(shù)(包含函數(shù)語句、函數(shù)表達(dá)式、Lambda表達(dá)式)會返回一個 Promise 對象,如果在函數(shù)中 return 一個直接量,async 會把這個直接量通過 Promise.resolve() 封裝成 Promise 對象。
async 函數(shù)返回的是一個 Promise 對象,所以在最外層不能用 await 獲取其返回值的情況下,我們當(dāng)然應(yīng)該用原來的方式:then() 鏈來處理這個 Promise 對象,就像這樣
testAsync().then(v => { console.log(v); // 輸出 hello async });
現(xiàn)在回過頭來想下,如果 async 函數(shù)沒有返回值,又該如何?很容易想到,它會返回 Promise.resolve(undefined)。
聯(lián)想一下 Promise 的特點(diǎn)——無等待,所以在沒有 await 的情況下執(zhí)行 async 函數(shù),它會立即執(zhí)行,返回一個 Promise 對象,并且,絕不會阻塞后面的語句。這和普通返回 Promise 對象的函數(shù)并無二致。
那么下一個關(guān)鍵點(diǎn)就在于 await 關(guān)鍵字了。
await 到底在等啥一般來說,都認(rèn)為 await 是在等待一個 async 函數(shù)完成。不過按語法說明,await 等待的是一個表達(dá)式,這個表達(dá)式的計(jì)算結(jié)果是 Promise 對象或者其它值(換句話說,就是沒有特殊限定)。
因?yàn)?async 函數(shù)返回一個 Promise 對象,所以 await 可以用于等待一個 async 函數(shù)的返回值——這也可以說是 await 在等 async 函數(shù),但要清楚,它等的實(shí)際是一個返回值。注意到 await 不僅僅用于等 Promise 對象,它可以等任意表達(dá)式的結(jié)果,所以,await 后面實(shí)際是可以接普通函數(shù)調(diào)用或者直接量的。所以下面這個示例完全可以正確運(yùn)行
function getSomething() { return "something"; } async function testAsync() { return Promise.resolve("hello async"); } async function test() { const v1 = await getSomething(); const v2 = await testAsync(); console.log(v1, v2); } test();await 等到了要等的,然后呢
await 等到了它要等的東西,一個 Promise 對象,或者其它值,然后呢?我不得不先說,await 是個運(yùn)算符,用于組成表達(dá)式,await 表達(dá)式的運(yùn)算結(jié)果取決于它等的東西。
如果它等到的不是一個 Promise 對象,那 await 表達(dá)式的運(yùn)算結(jié)果就是它等到的東西。
如果它等到的是一個 Promise 對象,await 就忙起來了,它會阻塞后面的代碼,等著 Promise 對象 resolve,然后得到 resolve 的值,作為 await 表達(dá)式的運(yùn)算結(jié)果。
async/await 幫我們干了啥 作個簡單的比較看到上面的阻塞一詞,心慌了吧……放心,這就是 await 必須用在 async 函數(shù)中的原因。async 函數(shù)調(diào)用不會造成阻塞,它內(nèi)部所有的阻塞都被封裝在一個 Promise 對象中異步執(zhí)行。
上面已經(jīng)說明了 async 會將其后的函數(shù)(函數(shù)表達(dá)式或 Lambda)的返回值封裝成一個 Promise 對象,而 await 會等待這個 Promise 完成,并將其 resolve 的結(jié)果返回出來。
現(xiàn)在舉例,用 setTimeout 模擬耗時的異步操作,先來看看不用 async/await 會怎么寫
function takeLongTime() { return new Promise(resolve => { setTimeout(() => resolve("long_time_value"), 1000); }); } takeLongTime().then(v => { console.log("got", v); });
如果改用 async/await 呢,會是這樣
function takeLongTime() { return new Promise(resolve => { setTimeout(() => resolve("long_time_value"), 1000); }); } async function test() { const v = await takeLongTime(); console.log(v); } test();
眼尖的同學(xué)已經(jīng)發(fā)現(xiàn) takeLongTime() 沒有申明為 async。實(shí)際上,takeLongTime() 本身就是返回的 Promise 對象,加不加 async 結(jié)果都一樣,如果沒明白,請回過頭再去看看上面的“async 起什么作用”。
又一個疑問產(chǎn)生了,這兩段代碼,兩種方式對異步調(diào)用的處理(實(shí)際就是對 Promise 對象的處理)差別并不明顯,甚至使用 async/await 還需要多寫一些代碼,那它的優(yōu)勢到底在哪?
async/await 的優(yōu)勢在于處理 then 鏈單一的 Promise 鏈并不能發(fā)現(xiàn) async/await 的優(yōu)勢,但是,如果需要處理由多個 Promise 組成的 then 鏈的時候,優(yōu)勢就能體現(xiàn)出來了(很有意思,Promise 通過 then 鏈來解決多層回調(diào)的問題,現(xiàn)在又用 async/await 來進(jìn)一步優(yōu)化它)。
假設(shè)一個業(yè)務(wù),分多個步驟完成,每個步驟都是異步的,而且依賴于上一個步驟的結(jié)果。我們?nèi)匀挥?setTimeout 來模擬異步操作:
/** * 傳入?yún)?shù) n,表示這個函數(shù)執(zhí)行的時間(毫秒) * 執(zhí)行的結(jié)果是 n + 200,這個值將用于下一步驟 */ function takeLongTime(n) { return new Promise(resolve => { setTimeout(() => resolve(n + 200), n); }); } function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(n) { console.log(`step2 with ${n}`); return takeLongTime(n); } function step3(n) { console.log(`step3 with ${n}`); return takeLongTime(n); }
現(xiàn)在用 Promise 方式來實(shí)現(xiàn)這三個步驟的處理
function doIt() { console.time("doIt"); const time1 = 300; step1(time1) .then(time2 => step2(time2)) .then(time3 => step3(time3)) .then(result => { console.log(`result is ${result}`); console.timeEnd("doIt"); }); } doIt(); // c:var est>node --harmony_async_await . // step1 with 300 // step2 with 500 // step3 with 700 // result is 900 // doIt: 1507.251ms
輸出結(jié)果 result 是 step3() 的參數(shù) 700 + 200 = 900。doIt() 順序執(zhí)行了三個步驟,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 計(jì)算的結(jié)果一致。
如果用 async/await 來實(shí)現(xiàn)呢,會是這樣
async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time2); const result = await step3(time3); console.log(`result is ${result}`); console.timeEnd("doIt"); } doIt();
結(jié)果和之前的 Promise 實(shí)現(xiàn)是一樣的,但是這個代碼看起來是不是清晰得多,幾乎跟同步代碼一樣
還有更酷的現(xiàn)在把業(yè)務(wù)要求改一下,仍然是三個步驟,但每一個步驟都需要之前每個步驟的結(jié)果。
function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(m, n) { console.log(`step2 with ${m} and ${n}`); return takeLongTime(m + n); } function step3(k, m, n) { console.log(`step3 with ${k}, ${m} and ${n}`); return takeLongTime(k + m + n); }
這回先用 async/await 來寫:
async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time1, time2); const result = await step3(time1, time2, time3); console.log(`result is ${result}`); console.timeEnd("doIt"); } doIt(); // c:var est>node --harmony_async_await . // step1 with 300 // step2 with 800 = 300 + 500 // step3 with 1800 = 300 + 500 + 1000 // result is 2000 // doIt: 2907.387ms
除了覺得執(zhí)行時間變長了之外,似乎和之前的示例沒啥區(qū)別啊!別急,認(rèn)真想想如果把它寫成 Promise 方式實(shí)現(xiàn)會是什么樣子?
function doIt() { console.time("doIt"); const time1 = 300; step1(time1) .then(time2 => { return step2(time1, time2) .then(time3 => [time1, time2, time3]); }) .then(times => { const [time1, time2, time3] = times; return step3(time1, time2, time3); }) .then(result => { console.log(`result is ${result}`); console.timeEnd("doIt"); }); } doIt();
有沒有感覺有點(diǎn)復(fù)雜的樣子?那一堆參數(shù)處理,就是 Promise 方案的死穴—— 參數(shù)傳遞太麻煩了,看著就暈!
洗洗睡吧就目前來說,已經(jīng)理解 async/await 了吧?但其實(shí)還有一些事情沒提及——Promise 有可能 reject 啊,怎么處理呢?如果需要并行處理3個步驟,再等待所有結(jié)果,又該怎么處理呢?
阮一峰老師已經(jīng)說過了,我就懶得說了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/80963.html
摘要:想閱讀更多優(yōu)質(zhì)文章請猛戳博客一年百來篇優(yōu)質(zhì)文章等著你引入的在的異步編程中是一個極好的改進(jìn)。可能會產(chǎn)生誤導(dǎo)一些文章將與進(jìn)行了比較,并聲稱它是下一代異步編程風(fēng)格,對此作者深表異議。結(jié)論引入的關(guān)鍵字無疑是對異步編程的改進(jìn)。 showImg(https://segmentfault.com/img/bVbjFP0?w=800&h=450); 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇...
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:對于引擎來說,兩者獲取堆棧的方式是不同的。對于引擎來說,堆棧信息附加在了函數(shù)所返回的并在鏈中傳遞,這樣函數(shù)也能在需要的時候獲取堆棧信息。使用可以實(shí)時監(jiān)控線上應(yīng)用的錯誤,并獲取完整的堆棧信息。 譯者按: Async/Await真的只是簡單的語法糖嗎?No! 原文:Asynchronous stack traces: why await beats .then() 作者: Math...
摘要:所以是在一秒后顯示的。這個行為不會耗費(fèi)資源,因?yàn)橐婵梢酝瑫r處理其他任務(wù)執(zhí)行其他腳本,處理事件等。每個回調(diào)首先被放入微任務(wù)隊(duì)列然后在當(dāng)前代碼執(zhí)行完成后被執(zhí)行。,函數(shù)是異步的,但是會立即運(yùn)行。否則,就返回結(jié)果,并賦值。 「async/await」是 promises 的另一種更便捷更流行的寫法,同時它也更易于理解和使用。 Async functions 讓我們以 async 這個關(guān)鍵字開...
閱讀 3921·2021-11-17 09:33
閱讀 3283·2021-10-08 10:05
閱讀 3111·2021-09-22 15:36
閱讀 1140·2021-09-06 15:02
閱讀 2772·2019-08-29 12:45
閱讀 1590·2019-08-26 13:40
閱讀 3399·2019-08-26 13:37
閱讀 420·2019-08-26 13:37