async/await的基礎(chǔ)用法

相對(duì)于回調(diào)函數(shù)來說颠印,Promise是一種相對(duì)優(yōu)雅的選擇昧诱。那么有沒有更好的方案呢?答案就是async/await潭辈。
優(yōu)勢(shì)主要體現(xiàn)在鸯屿,級(jí)聯(lián)調(diào)用,也就是幾個(gè)調(diào)用依次發(fā)生的場(chǎng)景把敢。
async/await寄摆。被稱為到目前最優(yōu)雅的異步過程解決方案,不知道你是否認(rèn)同修赞,反正我是信了婶恼。

相對(duì)于Promise桑阶,async/await有什么優(yōu)點(diǎn)?

比較場(chǎng)景: 級(jí)聯(lián)調(diào)用勾邦,也就是幾個(gè)調(diào)用依次發(fā)生的場(chǎng)景

  • Promise主要用then函數(shù)的鏈?zhǔn)秸{(diào)用蚣录,一直點(diǎn)點(diǎn)點(diǎn),是一種從左向右的橫向?qū)懛ā?br> async/await從上到下眷篇,順序執(zhí)行萎河,就像寫同步代碼一樣。這更符合人編寫代碼的習(xí)慣
  • Promisethen函數(shù)只能傳遞一個(gè)參數(shù)蕉饼,雖然可以通過包裝成對(duì)象虐杯,但是這會(huì)導(dǎo)致傳遞冗余信息,頻繁的解析又重新組合參數(shù)昧港,比較麻煩擎椰。
    async/await沒有這個(gè)限制,就當(dāng)做普通的局部變量來處理好了创肥,用let或者const定義的塊級(jí)變量达舒,想怎么用就怎么用,想定義幾個(gè)就定義幾個(gè)叹侄,完全沒有限制休弃,也沒有冗余的工作。
  • Promise在使用的時(shí)候最好將同步代碼和異步代碼放在不同的then節(jié)點(diǎn)中圈膏,這樣結(jié)構(gòu)更加清晰。
    async/await整個(gè)書寫習(xí)慣都是同步的篙骡,不需要糾結(jié)同步和異步的區(qū)別稽坤。當(dāng)然,異步過程需要包裝成一個(gè)Promise對(duì)象糯俗,放在await關(guān)鍵字后面尿褪,這點(diǎn)還是要牢記的。
  • Promise是根據(jù)函數(shù)式編程的范式得湘,對(duì)異步過程進(jìn)行了一層封裝杖玲。
    async/await是基于協(xié)程的機(jī)制,是真正的“保存上下文淘正,控制權(quán)切換 … … 控制權(quán)恢復(fù)摆马,取回上下文”這種機(jī)制,是對(duì)異步過程更精確的一種描述鸿吆。

進(jìn)程囤采、線程和協(xié)程的理解
上面的文章很好地解釋了這幾個(gè)概念的區(qū)別。
如果不糾結(jié)細(xì)節(jié)惩淳,可以簡(jiǎn)單地認(rèn)為:進(jìn)程 > 線程 > 協(xié)程蕉毯;
協(xié)程可以獨(dú)立完成一些與界面無關(guān)的工作,不會(huì)阻塞主線程渲染界面,也就是不會(huì)卡代虾。
協(xié)程进肯,雖然小一點(diǎn),不過能完成我們程序員交給的任務(wù)棉磨。而且我們可以自由控制運(yùn)行和阻塞狀態(tài)江掩,不需要求助于高大上的系統(tǒng)調(diào)度,這才是重點(diǎn)含蓉。

  • async/await是基于Promise的频敛,是進(jìn)一步的一種優(yōu)化。不過再寫代碼的時(shí)候馅扣,Promise本身的API出現(xiàn)得很少斟赚,很接近同步代碼的寫法。

await關(guān)鍵字使用時(shí)有哪些注意點(diǎn)差油?

  • 只能放在async函數(shù)內(nèi)部使用拗军,不能放在普通函數(shù)里面,否則會(huì)報(bào)錯(cuò)蓄喇。
  • 后面放Promise對(duì)象发侵,在Pending狀態(tài)時(shí),相應(yīng)的協(xié)程會(huì)交出控制權(quán)妆偏,進(jìn)入等待狀態(tài)刃鳄。這個(gè)是本質(zhì)。
  • awaitasync wait的意思钱骂,wait的是resolve(data)消息叔锐,并把數(shù)據(jù)data返回。比如见秽,下面代碼中愉烙,當(dāng)Promise對(duì)象由Pending變?yōu)?code>Resolved的時(shí)候,變量a就等于data解取;然后再順序執(zhí)行下面的語(yǔ)句console.log(a);

這真的是等待步责,真的是順序執(zhí)行,表現(xiàn)和同步代碼幾乎一模一樣禀苦。

const a = await new Promise((resolve, reject) => {
    // async process ...
    return resolve(data);
});
console.log(a);
  • await后面也可以跟同步代碼蔓肯,不過系統(tǒng)會(huì)自動(dòng)轉(zhuǎn)化成一個(gè)Promise對(duì)象。

比如

    const a = await 'hello world';

其實(shí)就相當(dāng)于

    const a = await Promise.resolve('hello world');

這跟同步代碼
const a = 'hello world';是一樣的伦忠,還不如省點(diǎn)事省核,去掉這里的await關(guān)鍵字。

  • await只關(guān)心異步過程成功的消息resolve(data)昆码,拿到相應(yīng)的數(shù)據(jù)data气忠。至于失敗消息reject(error)邻储,不關(guān)心,不處理旧噪。

當(dāng)然對(duì)于錯(cuò)誤消息的處理吨娜,有以下幾種方法供選擇:
(1)讓await后面的Promise對(duì)象自己catch
(2)也可以讓外面的async函數(shù)返回的Promise對(duì)象統(tǒng)一catch
(3)像同步代碼一樣,放在一個(gè)try...catch結(jié)構(gòu)中

async關(guān)鍵字使用時(shí)有哪些注意點(diǎn)淘钟?

  • 有了這個(gè)async關(guān)鍵字宦赠,只是表明里面可能有異步過程,里面可以有await關(guān)鍵字米母。當(dāng)然勾扭,全部是同步代碼也沒關(guān)系。當(dāng)然铁瞒,這時(shí)候這個(gè)async關(guān)鍵字就顯得多余了妙色。不是不能加,而是不應(yīng)該加慧耍。
  • async函數(shù)身辨,如果里面有異步過程,會(huì)等待芍碧;
    但是async函數(shù)本身會(huì)馬上返回煌珊,不會(huì)阻塞當(dāng)前線程。

可以簡(jiǎn)單認(rèn)為泌豆,async函數(shù)工作在主線程定庵,同步執(zhí)行,不會(huì)阻塞界面渲染踪危。
async函數(shù)內(nèi)部由async關(guān)鍵字修飾的異步過程洗贰,工作在相應(yīng)的協(xié)程上,會(huì)阻塞等待異步任務(wù)的完成再返回陨倡。

async函數(shù)的返回值是一個(gè)Promise對(duì)象,這個(gè)是和普通函數(shù)本質(zhì)不同的地方许布。這也是使用時(shí)重點(diǎn)注意的地方
(1)return newPromise();這個(gè)符合async函數(shù)本意兴革;
(2)return data;這個(gè)是同步函數(shù)的寫法,這里是要特別注意的蜜唾。這個(gè)時(shí)候杂曲,其實(shí)就相當(dāng)于Promise.resolve(data);還是一個(gè)Promise對(duì)象。
在調(diào)用async函數(shù)的地方通過簡(jiǎn)單的=是拿不到這個(gè)data的袁余。
那么怎么樣拿到這個(gè)data呢擎勘?
很簡(jiǎn)單,返回值是一個(gè)Promise對(duì)象颖榜,用.then(data => { })函數(shù)就可以棚饵。
(3)如果沒有返回煤裙,相當(dāng)于返回了Promise.resolve(undefined);

  • await是不管異步過程的reject(error)消息的,async函數(shù)返回的這個(gè)Promise對(duì)象的catch函數(shù)就負(fù)責(zé)統(tǒng)一抓取內(nèi)部所有異步過程的錯(cuò)誤噪漾。
    async函數(shù)內(nèi)部只要有一個(gè)異步過程發(fā)生錯(cuò)誤硼砰,整個(gè)執(zhí)行過程就中斷,這個(gè)返回的Promise對(duì)象的catch就能抓到這個(gè)錯(cuò)誤欣硼。
  • async函數(shù)執(zhí)行和普通函數(shù)一樣题翰,函數(shù)名帶個(gè)()就可以了,參數(shù)個(gè)數(shù)隨意诈胜,沒有限制豹障;也需要有async關(guān)鍵字。
    只是返回值是一個(gè)Promise對(duì)象焦匈,可以用then函數(shù)得到返回值血公,用catch抓去整個(gè)流程中發(fā)生的錯(cuò)誤。

基本套路

Step1:用Promise對(duì)象包裝異步過程括授,這個(gè)和Promise的使用一樣坞笙。只是參數(shù)個(gè)數(shù)隨意,沒有限制荚虚。

function sleep(ms) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('sleep for ' + ms + ' ms');
        }, ms);
    });
}

Step2:定義異步流程薛夜,可以將按照需要定制,就像寫同步代碼那樣

async function asyncFunction() {
    console.time('asyncFunction total executing:');
    const sleep1 = await sleep(2000);
    console.log('sleep1: ' + sleep1);
    const [sleep2, sleep3, sleep4]= await Promise.all([sleep(2000), sleep(1000), sleep(1500)]);
    console.log('sleep2: ' + sleep2);
    console.log('sleep3: ' + sleep3);
    console.log('sleep4: ' + sleep4);
    const sleepRace = await Promise.race([sleep(3000), sleep(1000), sleep(1000)]);
    console.log('sleep race: ' + sleepRace);
    console.timeEnd('asyncFunction total executing:');

    return 'asyncFunction done.'  // 這個(gè)可以不返回版述,這里只是做個(gè)標(biāo)記梯澜,為了顯示流程
}

Step3:像普通函數(shù)調(diào)用async函數(shù),在then函數(shù)中獲取整個(gè)流程的返回信息渴析,在catch函數(shù)統(tǒng)一處理出錯(cuò)信息

asyncFunction().then(data => {
    console.log(data);       // asyncFunction return 的內(nèi)容在這里獲取
}).catch(error => {
    console.log(error);      // asyncFunction 的錯(cuò)誤統(tǒng)一在這里抓取
});

console.log('after asyncFunction code executing....'); // 這個(gè)代表asyncFunction函數(shù)后的代碼晚伙,
                                                       // 顯示asyncFunction本身會(huì)立即返回,不會(huì)阻塞主線程

流程解析

上面的代碼執(zhí)行之后俭茧,輸出的log如下咆疗,顯示了代碼執(zhí)行流程

after asyncFunction code executing....
sleep1: sleep for 2000 ms
sleep2: sleep for 2000 ms
sleep3: sleep for 1000 ms
sleep4: sleep for 1500 ms
sleep race: sleep for 1000 ms
asyncFunction total executing:: 5006.276123046875ms
asyncFunction done.
  1. after asyncFunction code executing....代碼位置在async函數(shù)asyncFunction()調(diào)用之后,反而先輸出母债。這說明async函數(shù)asyncFunction()調(diào)用之后會(huì)馬上返回午磁,不會(huì)阻塞主線程。
  2. sleep1: sleep for 2000 ms這是第一個(gè)await之后的第一個(gè)異步過程毡们,最先執(zhí)行迅皇,也最先完成,說明后面的代碼衙熔,不論是同步和異步登颓,都在等他執(zhí)行完畢。
  3. sleep2 ~ sleep4這是第二個(gè)await之后的Promise.all()異步過程红氯。這是“比慢模式”框咙,三個(gè)sleep都完成后咕痛,再運(yùn)行下面的代碼,耗時(shí)最長(zhǎng)的是2000ms扁耐;
  4. sleep race: sleep for 1000 ms這是第三個(gè)await之后的Promise.race()異步過程暇检。這是“比快模式”,耗時(shí)最短sleep都完成后婉称,就運(yùn)行下面的代碼块仆。耗時(shí)最短的是1000ms
  5. asyncFunction total executing::5006.276123046875ms這是最后的統(tǒng)計(jì)總共運(yùn)行時(shí)間代碼王暗。三個(gè)await之后的異步過程之和1000(獨(dú)立的) + 2000(Promise.all) + 1000(Promise.race) = 5000ms
    這個(gè)和統(tǒng)計(jì)出來的5006.276123046875ms非常接近悔据。說明上面的異步過程,和同步代碼執(zhí)行過程一致俗壹,協(xié)程真的是在等待異步過程執(zhí)行完畢科汗。
  6. asyncFunction done.這個(gè)是async函數(shù)返回的信息,在執(zhí)行時(shí)的then函數(shù)中獲得绷雏,說明整個(gè)流程完畢之后參數(shù)傳遞的過程头滔。
log

異常處理

  • async標(biāo)注過的函數(shù),返回一個(gè)Promise對(duì)象涎显,采用.then().catch()的方式來進(jìn)行異常處理坤检,是非常自然的方法,也推薦這么做期吓。就像上面的step3那樣做早歇。
  • 另外一種方法,就是對(duì)于異步過程采用await關(guān)鍵字讨勤,采用同步的try{} catch(){}的方式來進(jìn)行異常處理箭跳。
  • 這里要注意的是await關(guān)鍵字只能用在async標(biāo)注的函數(shù)中,所以潭千,原來的函數(shù)谱姓,不管以前是同步的還是異步的,都要加上async關(guān)鍵字刨晴,比如componentDidMount()就要變?yōu)?code>async componentDidMount()才可以在內(nèi)部使用await關(guān)鍵字逝段,不過功能上沒有任何影響。
  • 另外割捅,采用同步的try{} catch(){}的方式,可以把同步帚桩,異步代碼都可以放在里面亿驾,有錯(cuò)誤都能抓到,比如null.length這種账嚎,也能抓到莫瞬。
async componentDidMount() { // 這是React Native的回調(diào)函數(shù)儡蔓,加個(gè)async關(guān)鍵字,沒有任何影響疼邀,但是可以用await關(guān)鍵字
    // 將異步和同步的代碼放在一個(gè)try..catch中喂江,異常都能抓到
    try {
        let array = null;
        let data = await asyncFunction();  // 這里用await關(guān)鍵字,就能拿到結(jié)果值旁振;否則获询,沒有await的話,只能拿到Promise對(duì)象
        if (array.length > 0) {  // 這里會(huì)拋出異常拐袜,下面的catch也能抓到
            array.push(data);
        }
    } catch (error) {
        alert(JSON.stringify(error))
    }
}

這里模擬的是網(wǎng)絡(luò)過程吉嚣。一般情況,array是一個(gè)數(shù)組蹬铺,用if (array.length > 0)判斷一下長(zhǎng)度尝哆,有值再處理,沒有問題甜攀。但是秋泄,一旦網(wǎng)絡(luò)出問題,array就是一個(gè)null规阀,平時(shí)工作很好的if (array.length > 0)判斷就會(huì)拋異常恒序,JS代碼就中斷,停止工作姥敛,會(huì)帶來意想不到的問題奸焙。

這里加了一個(gè)try..catch結(jié)構(gòu),這種異常就能捕獲彤敛,(這是同步代碼中的異常与帆,不能用.then().catch()抓到),根據(jù)異常信息,一般是null沒有length屬性墨榄,方便定位問題玄糟。這里的話用if (array && (array.length > 0))就會(huì)安全一點(diǎn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袄秩,一起剝皮案震驚了整個(gè)濱河市阵翎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌之剧,老刑警劉巖郭卫,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異背稼,居然都是意外死亡贰军,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蟹肘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來词疼,“玉大人俯树,你說我怎么就攤上這事》〉粒” “怎么了许饿?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舵盈。 經(jīng)常有香客問我陋率,道長(zhǎng),這世上最難降的妖魔是什么书释? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任翘贮,我火速辦了婚禮,結(jié)果婚禮上爆惧,老公的妹妹穿的比我還像新娘狸页。我一直安慰自己,他們只是感情好扯再,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布芍耘。 她就那樣靜靜地躺著,像睡著了一般熄阻。 火紅的嫁衣襯著肌膚如雪斋竞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天秃殉,我揣著相機(jī)與錄音坝初,去河邊找鬼。 笑死钾军,一個(gè)胖子當(dāng)著我的面吹牛鳄袍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吏恭,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼拗小,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了樱哼?” 一聲冷哼從身側(cè)響起哀九,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搅幅,沒想到半個(gè)月后阅束,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茄唐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年息裸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡界牡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漾抬,到底是詐尸還是另有隱情宿亡,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布纳令,位于F島的核電站挽荠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏平绩。R本人自食惡果不足惜圈匆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捏雌。 院中可真熱鬧跃赚,春花似錦、人聲如沸性湿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肤频。三九已至叹括,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宵荒,已是汗流浹背汁雷。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留报咳,地道東北人侠讯。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像少孝,于是被迫代替她去往敵國(guó)和親继低。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容