總目錄
從C#到TypeScript - async await
上兩篇分別說了Promise
和Generator
,基礎已經打好,現在可以開始講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.all
或Promise.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
的語法糖。
除了*
換成async
, yield
換成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());
});
};