摘要:異步函數(shù)是值通過事件循環(huán)異步執(zhí)行的函數(shù),它會(huì)通過一個(gè)隱式的返回其結(jié)果。
async 異步函數(shù) 不完全使用攻略 前言
現(xiàn)在已經(jīng)到 8012 年的尾聲了,前端各方面的技術(shù)發(fā)展也層出不窮,VueConf TO 2018 大會(huì) 也發(fā)布了 Vue 3.0的計(jì)劃。而在我們(我)的日常中也經(jīng)常用 Vue 來編寫一些項(xiàng)目。那么,就少不了 ES6 的登場了。那么話說回來,你真的會(huì)用 ES6 的 async 異步函數(shù)嗎?
1、async 介紹先上 MDN 介紹:https://developer.mozilla.org...
async function 用于聲明 一個(gè) 返回 AsyncFunction 對象的異步函數(shù)。異步函數(shù)是值通過事件循環(huán)異步執(zhí)行的函數(shù),它會(huì)通過一個(gè)隱式的 Promise 返回其結(jié)果。如果你的代碼使用了異步函數(shù),它的語法和結(jié)構(gòu)更像是標(biāo)準(zhǔn)的同步函數(shù)
人工翻譯:async 關(guān)鍵字是用于表示一個(gè)函數(shù)里面有異步操作的含義。它通過返回一個(gè) Promise 對象來返回結(jié)果它的最大的特點(diǎn)是:通過 async / await 將異步的操作,但是寫法和結(jié)構(gòu)卻是和我們平時(shí)寫的(同步代碼)是一樣
2、示范// 一般我們會(huì)把所有請求方法都定義在一個(gè)文件里,這里定義一個(gè)方法來模擬我們的日常請求 function fetch() { axios.get("/user?ID=12345") .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); }; // 然后在需要它的地方調(diào)用它 async function getUserInfo() { const info = await fetch(); return info; } getUserInfo().then(info => console.log(info));
我們可以看到,整個(gè)過程非常直觀和清晰,語句語義非常明確,整個(gè)異步操作看起來就像是同步一樣。如果看完上面的流程沒有問題的話,那我們接下來繼續(xù)深入的了解一下。
3、async Promise setTimeout(定時(shí)器) 的結(jié)合使用情況接下來給大家演示一道題目,這道題是我當(dāng)時(shí)面某條的面試題,估計(jì)和多人也見過,這道題非常經(jīng)典而且使用場景頁非常多,研究意義非常大,那么我在這里就給大家分享一下。
async function async1(){ console.log("async1 start") await async2() console.log("async1 end") } async function async2(){ console.log("async2") } console.log("script start") setTimeout(function(){ console.log("setTimeout") },0) async1(); new Promise(function(resolve){ console.log("promise1") resolve(); }).then(function(){ console.log("promise2") }) console.log("script end")
這里一共有 8 條 log 語句,先別復(fù)制到控制臺上,大家給20秒鐘的時(shí)間默念一下輸出的順序。
1..2.. .. .. 20
我先給上正確的答案:
script start async1 start async2 promise1 script end promise2 async1 end setTimeout
如果你的答案和上面的正確答案有所偏差,那么說明你對 async / await 的理解還是不夠深刻,希望你閱讀完我的這篇文章之后可以直面各種同步異步問題了(嘻嘻,這還不點(diǎn)個(gè)贊嘛)
我們再來回顧一下 MDN 對 async / await 的描述:
當(dāng)調(diào)用一個(gè) async 函數(shù)時(shí),會(huì)返回一個(gè) Promise 對象。當(dāng)這個(gè) async 函數(shù)返回一個(gè)值時(shí),Promise 的 resolve 方法會(huì)負(fù)責(zé)傳遞這個(gè)值;當(dāng) async 函數(shù)拋出異常時(shí),Promise 的 reject 方法也會(huì)傳遞這個(gè)異常值。
async 函數(shù)中可能會(huì)有 await 表達(dá)式,這會(huì)使 async 函數(shù)暫停執(zhí)行,等待 Promise 的結(jié)果出來,然后恢復(fù)async函數(shù)的執(zhí)行并返回解析值(resolved)。
async/await的用途是簡化使用 promises 異步調(diào)用的操作,并對一組 Promises執(zhí)行某些操作。正如Promises類似于結(jié)構(gòu)化回調(diào),async/await類似于組合生成器和 promises。
await
await 操作符用于等待一個(gè)Promise 對象。它只能在異步函數(shù) async function 中使用。
[return_value] = await expression;await 表達(dá)式會(huì)暫停當(dāng)前 async function 的執(zhí)行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回調(diào)的resolve函數(shù)參數(shù)作為 await 表達(dá)式的值,繼續(xù)執(zhí)行 async function。
若 Promise 處理異常(rejected),await 表達(dá)式會(huì)把 Promise 的異常原因拋出。
另外,如果 await 操作符后的表達(dá)式的值不是一個(gè) Promise,則返回該值本身。
其中非常重要的一句是:遇到 await 表達(dá)式時(shí),會(huì)讓 async 函數(shù) 暫停執(zhí)行,等到 await 后面的語句(Promise)狀態(tài)發(fā)生改變(resolved或者rejected)之后,再恢復(fù) async 函數(shù)的執(zhí)行(再之后 await 下面的語句),并返回解析值(Promise的值)
這么多 Promise 相關(guān)的內(nèi)容是因?yàn)閍sync / await 是建立在 Promise 的基礎(chǔ)上的呀~~
然后再來回頭看我們的題目,會(huì)發(fā)現(xiàn),有點(diǎn)不對勁啊
async1 end promise2
那是因?yàn)檫€有一個(gè)Promise.resolve 的點(diǎn)沒有考慮,這也是我中招的點(diǎn)
4、分析過程定義一個(gè)異步函數(shù) async1
定義一個(gè)異步函數(shù) async2
打印 ‘script start’ // *1
定義一個(gè)定時(shí)器(宏任務(wù),優(yōu)先級低于微任務(wù)),在0ms 之后輸出
執(zhí)行異步函數(shù) async1
打印 "async1 start" // *2
遇到await 表達(dá)式,執(zhí)行 await 后面的 async2
打印 "async2" // *3
返回一個(gè) Promise,跳出 async1 函數(shù)體
執(zhí)行 new Promise 里的語句
打印 ‘promise1‘ // *4
resolve() , 返回一個(gè) Promise 對象,把這個(gè) Promise 壓進(jìn)隊(duì)列里
打印 ’script end" // *5
同步棧執(zhí)行完畢
回到 async1 的函數(shù)體,async2 函數(shù)沒有返回 Promise,所以把要等async2 的值 resolve,把 Promise 壓進(jìn)隊(duì)列
執(zhí)行 new Promise 后面的 .then,打印 ’promise2‘ // *6
回到 async1 的函數(shù)體,await 返回 Promise.resolve() ,然后打印后面的 ’async1 end‘ // *7
最后執(zhí)行定時(shí)器(宏任務(wù)) setTimeout,打印 ’setTimeout‘ // *8
我對這段代碼的過程分析大致如上(如果有什么理解不對的地方請指出),這里有很關(guān)鍵而且是大家容易理解錯(cuò)誤的點(diǎn)是:很多人以為 await 會(huì)一直等待后面的表達(dá)式執(zhí)行完之后才會(huì)執(zhí)行后續(xù)代碼,實(shí)際上 await 是會(huì)先執(zhí)行后面的表達(dá)式,然后返回一個(gè)Promise,接著就跳出整個(gè) async 函數(shù)來執(zhí)行后面的代碼,也就是說執(zhí)行到 await 的時(shí)候,會(huì)有一個(gè) 讓出線程 的操作。等后面的同步站執(zhí)行完了之后,又會(huì)回到 async 函數(shù)中等待 await 表達(dá)式的返回值,如果不是一個(gè) Promise 對象,則會(huì)有一個(gè)期待它 resolve 成為一個(gè) Promise對象的過程,然后繼續(xù)執(zhí)行 async 函數(shù)后面的代碼,直到是一個(gè) Promise 對象,則把這個(gè) Promise 對象放入 Promise 隊(duì)列里。
所以說 ,’async1 end" 和‘promise2‘ 這個(gè)不注意就會(huì)出錯(cuò)的難點(diǎn)就是這樣
那么現(xiàn)在,我們是不是大致上對async / await 理解了呢,我們來改一下這道題再來看看,把 async2 改造一下
async function async1(){ console.log("async1 start") await async2() console.log("async1 end") } function async2(){ // 去掉了 async 關(guān)鍵字 console.log("async2"); } console.log("script start") setTimeout(function(){ console.log("setTimeout") },0) async1(); new Promise(function(resolve){ console.log("promise1") resolve(); }).then(function(){ console.log("promise2") }) console.log("script end")
這次大家能做對了嗎~
5、日常常用示例上面寫了那么多,只是為了方便大家對于異步函數(shù)的理解,
下面給一些我們?nèi)粘i_發(fā)中使用異步函數(shù)的例子。一般來說,我們有一個(gè)業(yè)務(wù)需要分不完成,每個(gè)步驟都是異步的,并且嚴(yán)重依賴于上一步的執(zhí)行結(jié)果,稍有不慎就會(huì)進(jìn)入回調(diào)地獄(callback hell)了,這種情況下,我們可以用 async / await 來完成
// 比如在這里場景,我們提交數(shù)據(jù)的時(shí)候先判定用戶是否有這個(gè)權(quán)限,然后再進(jìn)行下一步動(dòng)作 async function submitData(data) { const res = await getAuth(); // 獲取授權(quán)狀態(tài) if (res....) { const data = await submit(data); } toast(data.message); }
這樣就可以保證兩個(gè)操作的先后順序
或者是在 Vue 中,一些初始化的操作
async created() { const res = await this.init(); // 獲取列表等操作 const list = await this.getPage(); // 分頁請求等 }
但是在使用過程中,我們會(huì)發(fā)現(xiàn)剛從回調(diào)地獄中解救,然后就陷入 async / await 地獄的誕生
舉一個(gè)例子:
async created() { const userInfo = await this.getUserInfo(); // 獲取用戶數(shù)據(jù) const list = await this.getNewsList(); // 獲取文章數(shù)據(jù) }
表面上看,這段語法是正確的,但并不是一個(gè)優(yōu)秀實(shí)現(xiàn),因?yàn)樗褍蓚€(gè)沒有先后順序的一部操作強(qiáng)行變成同步操作了,因?yàn)檫@里的代碼是一行接著一行執(zhí)行的,想一下,我們沒有必要在獲取用戶數(shù)據(jù)之后才去獲取文章數(shù)據(jù),它們的工作是可以同時(shí)進(jìn)行的
這里給出一些常用的并發(fā)執(zhí)行的實(shí)例
async created() { const userInfo = this.getUserInfo(); // 它們都會(huì)返回 Promise 對象 const list = this.getNewsList(); await userInfo; await list; // ...do something } // 如果有很多請求的情況下可以使用 Promise.all async created() { Promise.all([this.getUserInfo(), this.getNewsList()]).then(()=> { // ...do something }); }5、圖例 6、小結(jié)
1、異步的終極解決方案
2、看起來像同步的異步操作
3、便捷的捕獲錯(cuò)誤和調(diào)試
4、支持并發(fā)執(zhí)行
5、要知道避免 async / await 地獄
7、寫在最后好了,關(guān)于async 異步函數(shù)的不完全指南就說到這里了,上面所提及的內(nèi)容,可能也就比較淺顯的內(nèi)容。而且有時(shí)候,建議大家熟練使用它,在日常開發(fā)中多使用多總結(jié)才會(huì)有沉淀的效果,都是要靠自己多練,才能熟悉使用,熟能生巧!
最后,如果大家覺得我有哪里寫錯(cuò)了,寫得不好,有其它什么建議(夸獎(jiǎng)),非常歡迎大家補(bǔ)充。希望能讓大家交流意見,相互學(xué)習(xí),一起進(jìn)步!
我是一名 19 的應(yīng)屆新人,以上就是今天的分享,新手上路中,后續(xù)不定期周更(或者是月更哈哈),我會(huì)努力讓自己變得更優(yōu)秀、寫出更好的文章,文章中有不對之處,煩請各位大神斧正。如果你覺得這篇文章對你有所幫助,請記得點(diǎn)贊或者品論留言哦~。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99662.html
摘要:安裝然后在的配置文件加入入口文件引入這樣就可以啦,還是可以減少很多代碼量的。是參數(shù),等同于執(zhí)行正常。這個(gè)包很簡單,就是引用了和,然后生產(chǎn)環(huán)境把它們編譯到目錄下,做了映射,供使用。 引入 這個(gè)問題是對自己的發(fā)問,但我相信會(huì)有很多跟我一樣的同學(xué)。對于 babel 的使用,近半年來一直停留在與 webpack 結(jié)合使用,以及在瀏覽器開發(fā)環(huán)境下。導(dǎo)致很多 babel 的包,我都不清楚他們是干嘛...
摘要:前端工程師自檢清單對于,掌握其語法和特性是最基本的,但是這些只是應(yīng)用能力,最終仍舊考量仍然是計(jì)算機(jī)體系的理論知識,所以數(shù)據(jù)結(jié)構(gòu),算法,軟件工程,設(shè)計(jì)模式等基礎(chǔ)知識對前端工程師同樣重要,這些知識的理解程度,可以決定你在前端工程師這條路上能走多 2019前端工程師自檢清單 對于JavaScript,掌握其語法和特性是最基本的,但是這些只是應(yīng)用能力,最終仍舊考量仍然是計(jì)算機(jī)體系的理論知識,所...
閱讀 1137·2023-04-26 00:12
閱讀 3283·2021-11-17 09:33
閱讀 1072·2021-09-04 16:45
閱讀 1203·2021-09-02 15:40
閱讀 2189·2019-08-30 15:56
閱讀 2976·2019-08-30 15:53
閱讀 3560·2019-08-30 11:23
閱讀 1941·2019-08-29 13:54