細(xì)說(shuō) async/await


其他相關(guān)傳送門(mén)

  1. Promise異步操作詳解
  2. Promise詳細(xì)用法
  3. 手寫(xiě)一個(gè)Promise

核心

1. 執(zhí)行 async 函數(shù),默認(rèn)返回一個(gè) promise 對(duì)象

2. await 相當(dāng)于 promise 的 then

3. try...catch 可捕獲異常谜诫,代替了 promise 的 catch


細(xì)說(shuō)

1. async

async 是ES7新出的特性碴卧,表明當(dāng)前函數(shù)是異步函數(shù),不會(huì)阻塞線程導(dǎo)致后續(xù)代碼停止運(yùn)行抖格。

async 函數(shù)诺苹,就是 Generator 函數(shù) 的語(yǔ)法糖

相較于 Generator雹拄,async 函數(shù)的改進(jìn)在于下面四點(diǎn):

  1. 內(nèi)置執(zhí)行器:Generator 函數(shù)的執(zhí)行必須依靠執(zhí)行器收奔,而 async 函數(shù)自帶執(zhí)行器,調(diào)用方式跟普通函數(shù)的調(diào)用一樣
  2. 更好的語(yǔ)義:asyncawait 相較于 *yield 更加語(yǔ)義化
  3. 更廣的適用性:co 模塊約定滓玖,yield 命令后面只能是 Thunk 函數(shù)或 Promise對(duì)象坪哄。而 async 函數(shù)的 await 命令后面則可以是 Promise 或者 原始類型的值(Numberstring势篡,boolean翩肌,但這時(shí)等同于同步操作)
  4. 返回值是 Promise:async 函數(shù)返回值是 Promise 對(duì)象,比 Generator 函數(shù)返回的 Iterator 對(duì)象方便禁悠,可以直接使用 then() 方法進(jìn)行調(diào)用
async function asyncFn() {
    console.log('這里是同步');
    return '我后執(zhí)行'; // 返回一個(gè) promise 對(duì)象,相當(dāng)于 return Promise.resolve('我后執(zhí)行')
}

console.log(asyncFn()); // Promise {<fulfilled>: "我后執(zhí)行"}

asyncFn().then(result => {
    console.log(result+'念祭,這里是異步');
})

console.log('我先執(zhí)行');

// 這里是同步
// 我先執(zhí)行
// 我后執(zhí)行,這里是異步

上面的執(zhí)行結(jié)果是先打印出'我先執(zhí)行'碍侦,雖然是上面asyncFn()先執(zhí)行粱坤,但是已經(jīng)被定義異步函數(shù)了隶糕,不會(huì)影響后續(xù)函數(shù)的執(zhí)行。

async 定義的函數(shù)內(nèi)部會(huì)默認(rèn)返回一個(gè) promise 對(duì)象:

  • 如果函數(shù)內(nèi)部發(fā)現(xiàn)不是異痴拘或者reject枚驻,則判定成功,這里可以return各種數(shù)據(jù)類型的值株旷,false,NaN,undefined...總之再登,都是resolve
  • 如果函數(shù)內(nèi)部拋出異常或者是返回reject灾常,都會(huì)使函數(shù)的promise狀態(tài)為失敗reject

async里霎冯,必須要將結(jié)果return出去,不然的話不管是執(zhí)行reject還是resolved的值都為undefine

//正確reject方法钞瀑。必須將reject狀態(tài)return出去沈撞。
async function PromiseError() {    
   return Promise.reject('has Promise Error');
}

//這是錯(cuò)誤的做法,并且判定resolve雕什,返回值為undefined,并且Uncaught報(bào)錯(cuò)
async function PromiseError() {
  Promise.reject('這是錯(cuò)誤的做法');
}

2. await

await 意思是 async wait(異步等待)缠俺。

  • await 后面接一個(gè)會(huì) return new promise 的函數(shù)并執(zhí)行它
  • await 只能放在 async 函數(shù)里

任何 async 函數(shù)都會(huì)默認(rèn)返回 promise,并且這個(gè) promise 解析的值都將會(huì)是這個(gè)函數(shù)的返回值贷岸,而 async 函數(shù)必須等到內(nèi)部所有的 await 命令的 Promise 對(duì)象執(zhí)行完壹士,才會(huì)發(fā)生狀態(tài)改變。

就是說(shuō)偿警,必須等所有await 函數(shù)執(zhí)行完畢后躏救,才會(huì)告訴promise我成功了還是失敗了,執(zhí)行then或者catch

function dice(){
    return new Promise((resolve, reject)=>{
        let sino = parseInt(Math.random() * 6 +1);
        setTimeout(()=>{
            resolve(sino);
        },3000);
    })
}
async function test(){
    let n =await dice(); // await 相當(dāng)于 Promise 的 then
    console.log(n);
}
test();

await和成功后的操作放到try里螟蒸,失敗的放在catch

function dice(val) {
    return new Promise((resolve, reject) => {
        let sino = parseInt(Math.random() * 6 + 1);
        if (sino > 3) {
            val === '大' ? resolve(sino) : reject(sino);
        } else {
            val === '大' ? reject(sino) : resolve(sino);
        }
    })
}
async function test() {
    // try...catch 可捕獲異常盒使,代替了 Promise 的 catch 
    try {
        //把a(bǔ)wait及獲取它的值的操作放在try里
        let n = await dice('大');  // await 相當(dāng)于 Promise 的 then
        console.log('贏了' + n);
    } catch (error) {
        //失敗的操作放在catch里
        console.log('輸了' + error); // 相當(dāng)于 Promise 的 catch
    }
}
test();

await 后面是 rejected 狀態(tài)的栗子:

(async function () {
    const p = Promise.reject('promise error');
    const res = await p; // await 相當(dāng)于 Promise 的 then, 但這里是rejected狀態(tài),會(huì)報(bào)錯(cuò)
    console.log(res);
})()

try...catch 捕獲異常即可:

(async function () {
    const p = Promise.reject('promise error');
    try {
        const res = await p;
        console.log(res);
    } catch (error) {
        console.log(error); // 正常輸出: promise error
    }
})()

3. await 等到之后七嫌,做了一件什么事情少办?

await 下面所有的代碼都是異步

await等到的結(jié)果分2種:

  • 不是promise對(duì)象
    如果不是 promise , await會(huì)阻塞后面的代碼,先執(zhí)行async外面的同步代碼诵原,同步代碼執(zhí)行完英妓,再回到async內(nèi)部,把這個(gè)非promise的對(duì)象绍赛,作為 await表達(dá)式的結(jié)果蔓纠。
async function async1() {
    await async2(); // 先執(zhí)行async2(),await下面所有的代碼都是異步
    console.log(1); // 異步
}
async function async2() {
    console.log(2);
}
console.log(3);
async1();

// 3
// 2
// 1
  • 是promise對(duì)象
    如果是 promise 對(duì)象吗蚌,await 也會(huì)阻塞async后面的代碼腿倚,先執(zhí)行async外面的同步代碼,等著 Promise 對(duì)象 fulfilled褪测,然后把 resolve 的參數(shù)作為 await 表達(dá)式的運(yùn)算結(jié)果猴誊。
function fn() {
    return new Promise(resolve => {
        console.log(1); // 同步2
        resolve();
    })
}
async function async1() {
    await fn().then(() => {  // 先執(zhí)行fn(),await下面所有的代碼都是異步
        console.log(2); // 異步1
    })
    console.log(3); // 異步1的下一個(gè)異步
}
console.log(4);  // 同步1
async1();

// 4
// 1
// 2
// 3

如果asycn里的代碼都是同步的侮措,那么這個(gè)函數(shù)被調(diào)用就會(huì)同步執(zhí)行

async function fn(){
  console.log('a'); // 同步1
}
fn();
console.log('b'); // 同步2

//a
//b
async function async1() {
    console.log(1); // 同步2
    await async2(); // 先執(zhí)行async2()  再await 
    console.log(2); // 異步(await下面所有的代碼都是異步)
}
async function async2() {
    console.log(3); // 同步3
}
console.log(4); // 同步1
async1();
console.log(5); // 同步4

// 4
// 1
// 3
// 5
// 2
async function async1() {
    console.log(1);
    await async2();
    console.log(2);
    await async3();
    console.log(3);
}

async function async2() {
    return new Promise((resolve) => {
        console.log(4);
        resolve(); // 如果沒(méi)有resolve()懈叹,await async2()后的代碼都不會(huì)執(zhí)行,只輸出6 1 4 7
    })
}

async function async3() {
    console.log(5);
}

console.log(6);

async1();

console.log(7);

// 6
// 1
// 4
// 7
// 2
// 5
// 3

4. 使用 async/await 改寫(xiě) then 鏈

相比于 Promise 分扎,async/await 能更好地處理 then鏈

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);
}

使用then的鏈?zhǔn)秸{(diào)用

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}`);
        });
        
}
doIt();

// step1 with 300
// step2 with 500
// step3 with 700
// result is 900

使用 async/await

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}`);
    
}
doIt();

// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
最后編輯于
?著作權(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)離奇詭異肾砂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宏悦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)镐确,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人饼煞,你說(shuō)我怎么就攤上這事源葫。” “怎么了砖瞧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵息堂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我块促,道長(zhǎng)荣堰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任褂乍,我火速辦了婚禮持隧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逃片。我一直安慰自己屡拨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布褥实。 她就那樣靜靜地躺著呀狼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪损离。 梳的紋絲不亂的頭發(fā)上哥艇,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音僻澎,去河邊找鬼貌踏。 笑死十饥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祖乳。 我是一名探鬼主播逗堵,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼眷昆!你這毒婦竟也來(lái)了蜒秤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤亚斋,失蹤者是張志新(化名)和其女友劉穎作媚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一泵殴、第九天 我趴在偏房一處隱蔽的房頂上張望涮帘。 院中可真熱鬧,春花似錦笑诅、人聲如沸调缨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)弦叶。三九已至,卻和暖如春妇多,著一層夾襖步出監(jiān)牢的瞬間伤哺,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(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)容