摘要:以往的異步方法無(wú)外乎回調(diào)函數(shù)和。當(dāng)然,對(duì)這個(gè)新特性也有一定的擔(dān)心,體現(xiàn)在他使得異步代碼變的不再明顯,我們好不容易已經(jīng)學(xué)會(huì)并習(xí)慣了使用回調(diào)函數(shù)或者來(lái)處理異步。
自從Node的7.6版本,已經(jīng)默認(rèn)支持async/await特性了。如果你還沒(méi)有使用過(guò)他,或者對(duì)他的用法不太了解,這篇文章會(huì)告訴你為什么這個(gè)特性“不容錯(cuò)過(guò)”。本文輔以大量實(shí)例,相信你能很輕松的看懂,并了解Javascript處理異步的一大殺器。
文章靈感和內(nèi)容借鑒了6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial),英文好的同學(xué)可以直接戳原版參考。
初識(shí)Async/await對(duì)于還不了解Async/await特性的同學(xué),下面一段是一個(gè)“速成”培訓(xùn)。
Async/await 是Javascript編寫(xiě)異步程序的新方法。以往的異步方法無(wú)外乎回調(diào)函數(shù)和Promise。但是Async/await建立于Promise之上。對(duì)于Javascript處理異步,是個(gè)老生常談卻歷久彌新的話題:
從最早的回調(diào)函數(shù),到 Promise 對(duì)象,再到 Generator 函數(shù),每次都有所改進(jìn),但又讓人覺(jué)得不徹底。它們都有額外的復(fù)雜性,都需要理解抽象的底層運(yùn)行機(jī)制。
異步編程的最高境界,就是根本不用關(guān)心它是不是異步。
async 函數(shù)就是隧道盡頭的亮光,很多人認(rèn)為它是異步操作的終極解決方案。
Async/await語(yǔ)法試想一下,我們有一個(gè)getJSON方法,該方法發(fā)送一個(gè)異步請(qǐng)求JSON數(shù)據(jù),并返回一個(gè)promise對(duì)象。這個(gè)promise對(duì)象的resolve方法傳遞異步獲得的JSON數(shù)據(jù)。具體例子的使用如下:
const makeRequest = () => getJSON() .then(data => { console.log(data) return "done" }) makeRequest()
在使用async/await時(shí),寫(xiě)法如下:
const makeRequest = async () => { console.log(await getJSON()) return "done" } makeRequest()
對(duì)比兩種寫(xiě)法,針對(duì)第二種,我需要進(jìn)一步說(shuō)明:
1)第二種寫(xiě)法(使用async/await),在主體函數(shù)之前使用了async關(guān)鍵字。在函數(shù)體內(nèi),使用了await關(guān)鍵字。當(dāng)然await關(guān)鍵字只能出現(xiàn)在用async聲明的函數(shù)體內(nèi)。該函數(shù)會(huì)隱式地返回一個(gè)Promise對(duì)象,函數(shù)體內(nèi)的return值,將會(huì)作為這個(gè)Promise對(duì)象resolve時(shí)的參數(shù)。
可以使用then方法添加回調(diào)函數(shù)。當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到await就會(huì)先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語(yǔ)句。
2)示例中,await getJSON() 說(shuō)明console.log的調(diào)用,會(huì)等到getJSON()返回的promise對(duì)象resolve之后觸發(fā)。
我們?cè)诳匆粋€(gè)例子加強(qiáng)一下理解,該例子取自阮一峰大神的《ECMAScript 6 入門》一書(shū):
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value); } asyncPrint("hello world", 50);
上面代碼指定50毫秒以后,輸出hello world。
Async/await究竟好在哪里?那么,同樣是處理異步操作,Async/await究竟好在哪里呢?
我們總結(jié)出以下6點(diǎn)。
我們看一下上面兩處代碼的代碼量,就可以直觀地看出使用Async/await對(duì)于代碼量的節(jié)省是很明顯的。對(duì)比Promise,我們不需要書(shū)寫(xiě).then,不需要新建一個(gè)匿名函數(shù)處理響應(yīng),也不需要再把數(shù)據(jù)賦值給一個(gè)我們其實(shí)并不需要的變量。同樣,我們避免了耦合的出現(xiàn)。這些看似很小的優(yōu)勢(shì)其實(shí)是很直觀的,在下面的代碼示例中,將會(huì)更加放大。
錯(cuò)誤處理Error handlingAsync/await使得處理同步+異步錯(cuò)誤成為了現(xiàn)實(shí)。我們同樣使用try/catch結(jié)構(gòu),但是在promises的情況下,try/catch難以處理在JSON.parse過(guò)程中的問(wèn)題,原因是這個(gè)錯(cuò)誤發(fā)生在Promise內(nèi)部。想要處理這種情況下的錯(cuò)誤,我們只能再嵌套一層try/catch,就像這樣:
const makeRequest = () => { try { getJSON() .then(result => { // this parse may fail const data = JSON.parse(result) console.log(data) }) // uncomment this block to handle asynchronous errors // .catch((err) => { // console.log(err) // }) } catch (err) { console.log(err) } }
但是,如果用async/await處理,一切變得簡(jiǎn)單,解析中的錯(cuò)誤也能輕而易舉的解決:
const makeRequest = async () => { try { // this parse may fail const data = JSON.parse(await getJSON()) console.log(data) } catch (err) { console.log(err) } }條件判別Conditionals
想象一下這樣的業(yè)務(wù)需求:我們需要先拉取數(shù)據(jù),然后根據(jù)得到的數(shù)據(jù)判斷是否輸出此數(shù)據(jù),或者根據(jù)數(shù)據(jù)內(nèi)容拉取更多的信息。如下:
const makeRequest = () => { return getJSON() .then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) }
這樣的代碼會(huì)讓我們看的頭疼。這這么多層(6層)嵌套過(guò)程中,非常容易“丟失自我”。
使用async/await,我們就可以輕而易舉的寫(xiě)出可讀性更高的代碼:
const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } }中間值Intermediate values
一個(gè)經(jīng)常出現(xiàn)的場(chǎng)景是,我們先調(diào)起promise1,然后根據(jù)返回值,調(diào)用promise2,之后再根據(jù)這兩個(gè)Promises得值,調(diào)取promise3。使用Promise,我們不難實(shí)現(xiàn):
const makeRequest = () => { return promise1() .then(value1 => { // do something return promise2(value1) .then(value2 => { // do something return promise3(value1, value2) }) }) }
如果你難以忍受這樣的代碼,我們可以優(yōu)化我們的Promise,方案是使用Promise.all來(lái)避免很深的嵌套。
就像這樣:
const makeRequest = () => { return promise1() .then(value1 => { // do something return Promise.all([value1, promise2(value1)]) }) .then(([value1, value2]) => { // do something return promise3(value1, value2) }) }
Promise.all這個(gè)方法犧牲了語(yǔ)義性,但是得到了更好的可讀性。
但是其實(shí),把value1 & value2一起放到一個(gè)數(shù)組中,是很“蛋疼”的,某種意義上也是多余的。
同樣的場(chǎng)景,使用async/await會(huì)非常簡(jiǎn)單:
const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) }錯(cuò)誤堆棧信息Error stacks
想象一下我們鏈?zhǔn)秸{(diào)用了很多promises,一級(jí)接一級(jí)。緊接著,這條promises鏈中某處出錯(cuò),如下:
const makeRequest = () => { return callAPromise() .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => { throw new Error("oops"); }) } makeRequest() .catch(err => { console.log(err); // output // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13) })
此鏈條的錯(cuò)誤堆棧信息并沒(méi)用線索指示錯(cuò)誤到底出現(xiàn)在哪里。更糟糕的事,他還會(huì)誤導(dǎo)開(kāi)發(fā)者:錯(cuò)誤信息中唯一出現(xiàn)的函數(shù)名稱其實(shí)根本就是無(wú)辜的。
我們?cè)倏匆幌耡sync/await的展現(xiàn):
const makeRequest = async () => { await callAPromise() await callAPromise() await callAPromise() await callAPromise() await callAPromise() throw new Error("oops"); } makeRequest() .catch(err => { console.log(err); // output // Error: oops at makeRequest (index.js:7:9) })
也許這樣的對(duì)比,對(duì)于在本地開(kāi)發(fā)階段區(qū)別不是很大。但是想象一下在服務(wù)器端,線上代碼的錯(cuò)誤日志情況下,將會(huì)變得非常有意義。你一定會(huì)覺(jué)得上面這樣的錯(cuò)誤信息,比“錯(cuò)誤出自一個(gè)then的then的then。。?!庇杏玫亩唷?/p> 調(diào)試Debugging
最后一點(diǎn),但是也是很重要的一點(diǎn),使用async/await來(lái)debug會(huì)變得非常簡(jiǎn)單。
在一個(gè)返回表達(dá)式的箭頭函數(shù)中,我們不能設(shè)置斷點(diǎn),這就會(huì)造成下面的局面:
const makeRequest = () => { return callAPromise() .then(()=>callAPromise()) .then(()=>callAPromise()) .then(()=>callAPromise()) .then(()=>callAPromise()) }
我們無(wú)法在每一行設(shè)置斷點(diǎn)。但是使用async/await時(shí):
const makeRequest = async () => { await callAPromise() await callAPromise() await callAPromise() await callAPromise() }總結(jié)
Async/await是近些年來(lái)JavaScript最具革命性的新特性之一。他讓讀者意識(shí)到使用Promise存在的一些問(wèn)題,并提供了自身來(lái)代替Promise的方案。
當(dāng)然,對(duì)這個(gè)新特性也有一定的擔(dān)心,體現(xiàn)在:
他使得異步代碼變的不再明顯,我們好不容易已經(jīng)學(xué)會(huì)并習(xí)慣了使用回調(diào)函數(shù)或者.then來(lái)處理異步。新的特性當(dāng)然需要時(shí)間成本去學(xué)習(xí)和體會(huì);
退回來(lái)說(shuō),熟悉C#語(yǔ)言的程序員一定會(huì)懂得這些學(xué)習(xí)成本是完全值得的。
Happy Coding!
PS: 作者Github倉(cāng)庫(kù),歡迎通過(guò)代碼各種形式交流。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/82492.html
摘要:以往的異步方法無(wú)外乎回調(diào)函數(shù)和。當(dāng)然,對(duì)這個(gè)新特性也有一定的擔(dān)心,體現(xiàn)在他使得異步代碼變的不再明顯,我們好不容易已經(jīng)學(xué)會(huì)并習(xí)慣了使用回調(diào)函數(shù)或者來(lái)處理異步。 自從Node的7.6版本,已經(jīng)默認(rèn)支持async/await特性了。如果你還沒(méi)有使用過(guò)他,或者對(duì)他的用法不太了解,這篇文章會(huì)告訴你為什么這個(gè)特性不容錯(cuò)過(guò)。本文輔以大量實(shí)例,相信你能很輕松的看懂,并了解Javascript處理異步的...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書(shū)了入門,覺(jué)得看看這本書(shū)就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(shū)(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書(shū)的目的是以目前還在制定中的ECMASc...
摘要:之前我們已經(jīng)開(kāi)發(fā)過(guò)一款小程序適用的音樂(lè)庫(kù),這次開(kāi)發(fā)網(wǎng)易云音樂(lè)庫(kù)的原因是音樂(lè)庫(kù)在小程序中環(huán)境下無(wú)法使用小程序提供的背景音頻播放器播放的問(wèn)題網(wǎng)易云的加密算法真的比其他幾家復(fù)雜太多了。。。 之前我們已經(jīng)開(kāi)發(fā)過(guò)一款小程序適用的qq音樂(lè)api庫(kù)https://github.com/FisherWY/Q...,這次開(kāi)發(fā)網(wǎng)易云音樂(lè)api庫(kù)的原因是qq音樂(lè)api庫(kù)在小程序中iOS環(huán)境下無(wú)法使用小程序提...
摘要:示例運(yùn)行函數(shù)彈出彈出函數(shù)接收參數(shù),返回值。其中,返回一個(gè)對(duì)象,是的返回值,代表函數(shù)是否執(zhí)行完成。 ES6特性介紹(下) ES6新的標(biāo)準(zhǔn),新的語(yǔ)法特征:1、變量/賦值2、函數(shù)3、數(shù)組/json4、字符串5、面向?qū)ο?、Promise7、generator8、ES7:async/await 《【W(wǎng)eb全棧課程二】ES6特性介紹(上)》見(jiàn):https://segmentfault.com/a...
閱讀 559·2023-04-25 17:26
閱讀 1560·2021-08-05 09:58
閱讀 2019·2019-08-30 13:17
閱讀 1014·2019-08-28 17:52
閱讀 1127·2019-08-26 18:27
閱讀 1467·2019-08-26 14:05
閱讀 3681·2019-08-26 14:05
閱讀 1683·2019-08-26 10:45