詳解JavaScript異步編程之a(chǎn)sync和await
當前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
經(jīng)過了Generator的過渡之后異步代碼同步化的需求逐漸成為了主流需求,雖然Generator函數(shù)能夠?qū)崿F(xiàn)異步編程,但實際上我們很少用它來實現(xiàn)異步,因為在ES7版本中得到了提案,并在ES8版本中進⾏了實現(xiàn)的 async 函數(shù)對Generator函數(shù)的流程又做了一層封裝,定義了全新的異步控制流程,使得異步方案使用更加方便。
async/await的代碼結(jié)構(gòu)的編寫⽅式與Generator函數(shù)結(jié)構(gòu)很相似,async就相當于那個(*),await就相當于yield。提案中規(guī)定了可以使⽤async修飾⼀個函數(shù),這樣就能在該函數(shù)的直接⼦作⽤域中,使⽤await來⾃動的控制函數(shù)的流程,await 右側(cè)可以編寫任何變量或?qū)ο螅斢覀?cè)是普通對象的時候函數(shù)會⾃動返回右側(cè)的結(jié)果并向下執(zhí)⾏,⽽當await右側(cè)為Promise對象時,如果Promise對象狀態(tài)沒有變成完成,函數(shù)就會掛起等待,直到Promise對象變成fulfilled,程序再向下執(zhí)⾏,并且Promise的值會⾃動返回給await左側(cè)的變量中。async和await需要成對出現(xiàn),async可以單獨修飾函數(shù),但是await只能在被async修飾的函數(shù)中使⽤。 有了await和async就相當于使⽤了⾃帶執(zhí)⾏函數(shù)的Generator函數(shù),這樣我們就不再需要單獨針對Generator函數(shù)進⾏開發(fā)了,所以async和await逐漸成為主流異步流程控制的終極解決⽅案。⽽Generator慢慢淡出了業(yè)務開發(fā)者的舞臺,不過Generator函數(shù)成為了向下兼容過渡期版本瀏覽器的候補實現(xiàn)⽅式,雖然在現(xiàn)今的⼤部分項⽬業(yè)務中使⽤Generator函數(shù)的場景⾮常的少,但是如果查看腳⼿架項⽬中通過babel構(gòu)建的JavaScript⽣產(chǎn)代碼,我們還是能⼤量的發(fā)現(xiàn)Generator的應⽤的,它的作⽤就是為了兼容不⽀持async和await的瀏覽器。 async的英文意思是異步,當函數(shù)前面有async關(guān)鍵字并且該函數(shù)有返回值時,函數(shù)執(zhí)行成功,函數(shù)就會調(diào)用Promise.resove()并隱式的返回一個Promise對象;如果函數(shù)執(zhí)行失敗就會調(diào)用Promise.reject()并返回一個Promise對象。
根據(jù)控制臺結(jié)果我們發(fā)現(xiàn)其實async修飾的函數(shù),本身就是⼀個Promise對象,雖然我們在函數(shù)中return的值是1,是使⽤了async修飾之后,這個函數(shù)運⾏時并沒有直接返回1,⽽是返回了⼀個值為1的Promise對象。 async函數(shù)中如果有異步操作會進行等待,但是async函數(shù)本身會馬上返回,不會阻塞當前線程。async函數(shù)被調(diào)用不會阻塞界面渲染,內(nèi)部由await關(guān)鍵字修飾異步過程,會阻塞等待異步任務的完成再返回。 如果在函數(shù)中return一個直接量,async會把這個直接量通過Promise.resolve(直接量) 封裝成 Promise 對象 ,如果沒有返回值,相當于返回了Promise.resolve(undefined)。 我們可以通過Promise.then()回調(diào)得到async函數(shù)的返回值,因為該函數(shù)返回的是Promise對象。
只有async函數(shù)內(nèi)部的異步操作執(zhí)行完,才會執(zhí)行then方法指定的回調(diào)函數(shù)。 看下面代碼:
通過控制臺看到打印順序為1、3、2。按照Promise對象的執(zhí)⾏流程function被async修飾之后它本身應該變成異步函數(shù),那么它應該在1和2輸出完畢之后再輸出3,但是結(jié)果卻出⼈意料,這是為什么呢? 我們回想一下Promise函數(shù)的結(jié)構(gòu)。
在介紹Promise對象時,我們知道new Promise時的function是同步流程,而then()是異步的,這也就不難解釋為什么輸出結(jié)果是1、3、2了。 await的英文意思是等待,等待的是一個表達式,這個表達式的計算結(jié)果是 Promise 對象或者其它值,得到resolve的值作為await表達式的運算結(jié)果。 因為 async 函數(shù)返回一個 Promise 對象,所以 await 可以用于等待一個 async 函數(shù)的返回值,這也可以說是 await 在等 async 函數(shù),實際上它等待的是一個返回值。await 不僅僅用于等 Promise 對象,它可以等任意表達式的結(jié)果。 await 表達式的運算結(jié)果取決于它等的東西。如果它等到的不是一個 Promise 對象,相當于 await Promise.resolve(...),那 await 表達式的運算結(jié)果就是它等到的東西。 如果它等到的是一個 Promise 對象,它會阻塞后面的代碼,等著 Promise 對象 resolve,然后得到 resolve 的值,作為 await 表達式的運算結(jié)果。 依然以上面的代碼為例,稍加改造。
通過控制臺我們可以看到打印順序為1->3->2->4??梢钥吹絘wait 4表達式會將4作為其運算結(jié)果賦值給a,并且await會阻塞后面的console.log(a)的執(zhí)行,所以最后才會打印出4,這就是 await 必須用在 async 函數(shù)中的原因。 我們將上面的函數(shù)翻譯⼀下,由于async修飾的函數(shù)會被解釋成Promise對象,所以我們可以將其翻譯成如下結(jié)構(gòu):
看到這個Promise對象我們就明白了,由于初始化的回調(diào)是同步的所以1,3,2都是同步代碼,⽽4是在resolve中傳⼊的,then代表異步回調(diào)所以4應該最后輸出。 綜上所述,async函數(shù)中有⼀個最大的特點,就是第⼀個await會作為分⽔嶺⼀般的存在,在第⼀個await的右側(cè)和上⾯的代碼,全部是同步代碼區(qū)域相當于new Promise的回調(diào),第⼀個await的左側(cè)和下⾯的代碼,就變成了異步代碼區(qū)域相當于then的回調(diào)。 假設(shè)一個業(yè)務,分多個步驟完成,每個步驟都是異步的,而且依賴于上一個步驟的結(jié)果。在過去的編程中JavaScript的主要異步處理⽅式,是采⽤回調(diào)函數(shù)的⽅式來進⾏處理,想要保證n個步驟的異步編程有序進⾏,會出現(xiàn)如下的代碼。
雖然可以Promise 通過 then 鏈來解決多層回調(diào)的問題,但是現(xiàn)在有了async/await我們可使用它來進一步優(yōu)化上面的代碼。
用 Promise 方式來實現(xiàn)這三個步驟。
如果用 async/await 來實現(xiàn)如下。
現(xiàn)在我們可以使⽤如上的⽅式來進⾏流程控制,不再需要依賴自己定義的流程控制器函數(shù)來進⾏分步執(zhí)⾏,這⼀切的核⼼起源都是Promise對象的規(guī)則定義開始的。使用async/await結(jié)果和之前的 Promise 實現(xiàn)是一樣的,但是這個代碼看起來是不是清晰得多,幾乎跟同步代碼一樣。 1、await只能用在async函數(shù)之中,也就是說await必須和async一起使用,反之,async可以單獨只用。2、await后面跟著是一個Promise對象,會等待Promise返回結(jié)果了,再繼續(xù)執(zhí)行后面的代碼。
3、await后面跟著的是一個數(shù)值或者是字符串等數(shù)據(jù)類型的值,則直接返回該值。 4、await后面跟著的是定時器,不會等待定時器里面的代碼執(zhí)行完,而是直接執(zhí)行后面的代碼,然后再執(zhí)行定時器中的代碼。
5、可以直接用標準的try...catch...語法捕捉錯誤。
從回調(diào)地獄到Promise的鏈式調(diào)⽤到Generator函數(shù)的分步執(zhí)⾏再到async和await的⾃動異步代碼同步化機制,經(jīng)歷了很多個年頭,所以⾯試中為什么經(jīng)常問到Promise,并且重點沿著Promise對象深⼊的挖掘去問你各種問題,主要是考察程序員對Promise對象本身以及他的發(fā)展歷程是否有深⼊的了解,同時也是在考察⾯試者對JavaScript的事件循環(huán)系統(tǒng)和異步編程的基本功是否⾜夠的扎實。Promise和事件循環(huán)系統(tǒng)并不是JavaScript中的⾼級知識,⽽是真正的基礎(chǔ)知識,所以所有⼈想要在⾏業(yè)中更好的發(fā)展下去,這些知識都是必備基礎(chǔ),必須扎實掌握。 該文章在 2024/4/1 15:05:16 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |