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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM