es6之async函數

什麼是async函數

async函數就是Generator函數的語法糖。async函數的實現原理就是Generator函數 + 自動執行器包裝在一個函數裏。es6

asycn函數 Generator函數
自帶執行器 執行必須靠執行器
async/await語義清晰 星號和yield語義不明確
await命令後Promise對象和原型類型值 Thunk函數或Promise
返回值Promise,可使用then命令 返回值Iterator對象

怎麼使用

aysnc函數返回一個Promise對象,aysnc函數內部return語句返回值,做爲then方法回調函數的參數。當函數執行時候,一旦遇到await就會先返回,等到異步操做完成,再接着執行函數體內後面的語句。ajax

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

async function foo(ms){
    await sleep(ms);
    return 300;
}

foo(2000).then((data)=> {
    console.log(data)
});
//300
複製代碼

調用foo會當即返回一個promise,2s後await後面的promise狀態變成resolve,才執行return,執行then,打印300。promise

因爲async返回是promise,程序能夠改成bash

async function sleep(ms) {
    await new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
}
//...其餘代碼同上
複製代碼

await命令後是一個Promise對象。若是不是,會被轉成一個當即resolve的Promise。若是await命令後面是一個thenable對象(即定義then方法的對象),那麼await會將其等同於 Promise 對象。能夠參照es6之Promise深刻理解併發

let thenable = {
    then: (resolve) => {
        console.log('thenable');
        resolve('xxx');
    }
}
async function foo(){
    return await thenable;
}

foo().then((data)=> {
    console.log(data)
}).catch((e)=>{
    console.log(e.toString())
});

//thenable
//xxx
複製代碼

錯誤處理機制

async函數內部拋出錯誤,外部catch捕獲

內部拋出錯誤,後面語句不執行,直接外部catch捕獲錯誤異步

async function foo(){
    throw new Error('出錯了');
    await sleep(); //不執行
}

foo().then((data)=> {
    console.log(data)
}).catch((e)=>{
    console.log(e.toString())
});

//Error: 出錯了
複製代碼

這種狀況會致使返回的Promise對象狀態爲reject狀態,錯誤被catch方法回調函數接收到。async

async函數返回的Promise對象必須等到內部全部await命名後面的Promise對象執行完纔會發生狀態改變,除非遇到return語句或者拋出錯誤。函數

任何一個await語句後面的Promise對象變爲reject狀態,那麼整個async函數都會中斷執行。post

async函數內部拋出錯誤,內部catch捕獲

內部本身捕獲,程序繼續往下執行return語句ui

async function foo(){
    await Promise.reject('error').catch((e)=>{
        console.log('in:', e.toString())
    });
    return 300;
}

foo().then((data)=> {
    console.log(data)
}).catch((e)=>{
    console.log('out:',e.toString())
});
//in: error
//300
複製代碼

try...catch 實現屢次重複嘗試

const NUM_RETRIES = 3;
async function multiRetries(url, count){
    let c = count || NUM_RETRIES;
    for(let i = 0; i < c; i++){
        try {
            await ajax.get(url);
            break; //請求成功就跳出循環
        } catch (error) {
            console.error(error);
        }
    }
}

multiRetries('http://xxxx').then((data)=> {
    console.log(data)
}).catch((e)=>{
    console.log(e.toString())
});
複製代碼

注意點

多個await命令後面的異步操做,若是不存在繼發關係,最好讓他們同時觸發

let [foo, bar] = await Promise.all([ajax.get(), ajax.post()])

//或
let fooPromise = ajax.get();
let barPromise = ajax.get();

let foo = await fooPromise;
let bar = await fooPromise;
複製代碼

forEach中使用await中注意,以下是併發的。由於只有async函數內部是繼發執行的,外部不受影響。

urls.forEach(async function(url){
    await ajax.get(url);
});

複製代碼

異步遍歷器

Iterator 接口是一種數據遍歷的協議,只要調用遍歷器對象的 next 方法就會獲得一個對象。特別注意 next 方法必須是同步的,只要調用就必須馬上返回值。

Generator函數裏面的異步操做返回一個Thunk函數或者Promise對象,等待之後返回真正的值,done屬性仍是同步產生的。

for await ... of

async function foo(){
    try {
        for await (let value of createAsyncIterable()){
            console.log(value);
        }
    } catch (e) {
        console.log(e.toString());
    }
}
複製代碼

異步Generator函數

await後面操做的應該返回Promise對象。使用yield關鍵字的地方,就是next方法停下來的地方。

async function* gen(){
    yield await Promise.resolve('xxx');
}
複製代碼

注:讀阮一峯老師的《ES6入門標準》作的筆記

相關文章
相關標籤/搜索