從C#到TypeScript - async await

總目錄

從C#到TypeScript - async await

上兩篇分別說了PromiseGenerator,基礎已經打好,如今能夠開始講async await了。
async await是ES7的議案,TypeScript在1.7版本開始支持async await編譯到ES6,並在2.1版本支持編譯到ES5和ES3,算是全面支持了。

async await 用法

和C#裏的十分類似,看個例子:

function delay(): Promise<void>{
    return new Promise<void>((resolve, reject)=>{setTimeout(()=>resolve(), 2000)});
}

async function run(){
    console.info('start');
    await delay();
    console.info('finish');
}

run();
console.info('run');

上面代碼執行的結果是執行完run()後當即返回一個Promise,遇到await跳出函數,繼續往下走,因此先輸出start,再緊接着輸出run,過了2秒後再輸出finish
能夠看到run函數,function前面多了個async(若是是class裏的方法,則是在函數名前),delay()前面多了個await,表示的意思很明顯,就是在二者之間等待2秒。
run函數返回的也是一個Promise對象,後面能夠接then來作後續操做。
await必需要在async塊中,await的對象能夠是Promise對象也能夠不是,不是的話會自動轉爲已經resolved的Promise對象。
另外,await在代碼塊中是按順序執行的,前面wait完後再會走下一步,若是須要並行執行,能夠和Promise同樣,用Promise.allPromise.race來達到目的。

async function run1(){
    await delay();
    console.info('run1');
}
async function run2(){
    await delay();
    console.info('run2');
}
async function run3(){
    await delay();
    console.info('run3');
}
Promise.all([run1(), run2(), run3()]);

上面代碼會在兩秒後幾乎同時輸出run1, run2, run3。

async返回Promise狀態

一個async函數中能夠有N個await,async函數返回的Promise則是由函數裏全部await一塊兒決定,只有全部await的狀態都resolved以後,async函數纔算真正完成,返回的Promise的狀態也變爲resolved。
固然若是中間return了或者出了異常仍是會中斷的。

async function run(){
    console.info('start');
    await delay();
    console.info('time 1');
    await delay();
    console.info('time 2');
    return;
    //下面固然就不會執行了
    await delay();
    console.info('time 3');
}

run的狀態在time 2輸出後return就轉爲resolved了。
當這裏出異常時,async函數會中斷並把異常返回到Promise裏的reject函數。

async function run(){
    await Promise.reject('error'); // 這裏出異常
    console.info('continue'); // 不會執行到這裏
    await delay();
}

異常處理

以前有提到Promise的異常能夠在後面用catch來捕獲,async await也同樣。
向上面的例子,可能有須要把整個函數即便出異常也要執行完,就能夠這樣作:

async function run(){
    await Promise.reject('error').catch(e=>console.info(e));
    console.info('continue'); // 繼續往下執行
    await delay();
}

let g = run(); //這裏的g也是成功的,由於異常已經被處理掉

若是是多個異常須要處理,能夠用try...catch

async function run(){
    try{
        await Promise.reject('error1');
        await Promise.reject('error2');
    } catch(e){
        console.info(e);
    }
    console.info('continue'); // 繼續往下執行
    await delay();
}

async await原理

前篇有說過async await實際上是Generator的語法糖。
除了*換成asyncyield換成await以外,最主要是async await內置了執行器,不用像Generator用那樣next()一直往下執行。
其實也就是async await內部作了co模塊作的事。

先來看看async await在TypeScript翻譯後的結果:

async function run(){
    await delay();
    console.info('run');
}
//翻譯成
function run() {
    return __awaiter(this, void 0, void 0, function* () {
        yield delay();
        console.info('run');
    });
}

能夠注意到其實仍是用__await()包了一個Generator函數,__await()的實現其實和上篇的co模塊的實現基本一致:

var __awaiter = (this && this.__awaiter) ||
function(thisArg, _arguments, P, generator) {
    return new(P || (P = Promise))(function(resolve, reject) {
        function fulfilled(value) { // 也是fulfilled,resolved的別名
            try {
                step(generator.next(value)); // 關鍵仍是這個step,裏面遞歸調用fulfilled
            } catch (e) {
                reject(e);
            }
        }

        function rejected(value) {
            try {
                step(generator["throw"](value));
            } catch (e) {
                reject(e);
            }
        }

        function step(result) {
            result.done ? resolve(result.value) : new P(function(resolve) { //P是Promise的類型別名
                resolve(result.value);
            }).then(fulfilled, rejected); // 沒有done的話繼續fulfilled
        }
        step((generator = generator.apply(thisArg, _arguments)).next());
    });
};
相關文章
相關標籤/搜索