1. async函數(shù)的基本形式
//函數(shù)聲明asyncfunction foo() {}//函數(shù)表達式const foo = asyncfunction () {};//對象的方法let obj = { async foo() {} };
obj.foo().then(...)//Class 的方法class Storage {
constructor() {
? ? this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
? ? const cache = awaitthis.cachePromise;
? ? returncache.match(`/avatars/${name}.jpg`);
}
}
const storage =new Storage();
storage.getAvatar('jake').then(…);//箭頭函數(shù)const foo = async () => {};
2. async函數(shù)的返回值總是一個Promise
無論async函數(shù)有無await操作,其總是返回一個Promise。
1. 沒有顯式return呕诉,相當于return Promise.resolve(undefined);
2. return非Promise的數(shù)據(jù)data,相當于return Promise.resolve(data);
3. return Promise, 會得到Promise對象本身
async總是返回Promise泽谨,因此兔毒,其后面可以直接調(diào)用then方法咖楣,
函數(shù)內(nèi)部return返回的值等龙,會成為then回調(diào)函數(shù)的參數(shù)
函數(shù)內(nèi)部拋出的錯誤处渣,會被then的第二個函數(shù)或catch方法捕獲到
//正常返回值asyncfunction f(){
? ? retrun 'hello world';
}
f().then(v => console.log(v));//hello world//拋出錯誤asyncfunction f(){
? ? thrownewError('出錯了');
}
f().then(
? ? v => console.log(v),
? ? e => console.log(e)//Error: 出錯了)
3. await操作符的值
[rv] = await expression(expression可以是任何值,通常是一個promise)
expression是Promise蛛砰,rv等于Promise兌現(xiàn)的值罐栈,若Promise被拒絕,則拋出異常泥畅,由catch捕獲
expression是非Promise荠诬,會被轉換為立即resolve的Promise,rv等于expression
await操作只能用在async函數(shù)中位仁,否則會報錯柑贞。
4. async就是generator和promise的語法糖
//generator寫法vargen =function* () {
? varf1 = yield readFile('/etc/fstab');
? varf2 = yield readFile('/etc/shells');
? console.log(f1.toString());
? console.log(f2.toString());
};//async寫法varasyncReadFile = asyncfunction () {
? varf1 = await readFile('/etc/fstab');
? varf2 = await readFile('/etc/shells');
? console.log(f1.toString());
? console.log(f2.toString());
};
async就是將 generator的 * 換成 async,將 yield 換成 await聂抢。
5. async對generator的改進
1. 內(nèi)置執(zhí)行器
Generator必須依靠執(zhí)行器調(diào)用next方法來自動執(zhí)行钧嘶,例如co模塊。而async函數(shù)自帶執(zhí)行器琳疏,可以自動執(zhí)行康辑。
2. 更好的語義
async和await分別表示異步和等待摄欲,語義更加明確
3. 適用性更強
co模塊后面只能是Thunk函數(shù)或Promise對象轿亮,而await后面可以是Promise或基本數(shù)據(jù)類型(如:數(shù)字疮薇,字符串,布爾等)
4. 返回Promise我注,可以繼續(xù)操作
async函數(shù)總是返回一個Promise對象按咒,可以對其進行then調(diào)用,繼續(xù)操作后面的數(shù)據(jù)但骨,因此励七,
async函數(shù)完全可以看作是多個Promise合成一個Promise對象,而await命令就是內(nèi)部的then調(diào)用奔缠。
6. async內(nèi)部的并行調(diào)用
async配合await都是串行調(diào)用掠抬,但是若有并行調(diào)用,則應按照以下方式來寫:
1. 變量分別接收Promise
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise();
let bar = await barPromise();
2. 使用Promise.all
let [foo,bar] = await Promise.all([getFoo(),getBar()]);
Promise.all這種寫法有缺陷校哎,一個調(diào)用報錯两波,會終止,這個不太符合并行調(diào)用的初衷闷哆。
3. 使用多個async函數(shù)
實際上腰奋,一個async函數(shù)內(nèi)部包含的調(diào)用應該是強相關的,沒有依賴關系的函數(shù)調(diào)用不應該放在一個async函數(shù)中抱怔,分開來邏輯更清晰劣坊。
4. 并行執(zhí)行的一些寫法
1. 不能再內(nèi)部非async function中使用await
asyncfunction dbFuc(db) {
? let docs = [{}, {}, {}];
? // 報錯,forEach的function是非async屈留,不能使用awaitdocs.forEach(function (doc) {
? ? await db.post(doc);
? });
}//這里不需要 asyncfunction dbFuc(db) {
? let docs = [{}, {}, {}];
? // 可能得到錯誤結果局冰,這樣調(diào)用也不能得到正確的結果docs.forEach(asyncfunction (doc) {
? ? await db.post(doc);
? });
}
2. 循環(huán)調(diào)用await可以使用for循環(huán)或for of循環(huán)
//for ofasyncfunction dbFuc(db) {
? let docs = [{}, {}, {}];
? for (let doc of docs) {
? ? await db.post(doc);
? }
}//map + Promise.allasyncfunction dbFuc(db) {
? let docs = [{}, {}, {}];
? let promises = docs.map((doc) => db.post(doc));
? let results = await Promise.all(promises);
? console.log(results);
}//map + for ofasyncfunction dbFuc(db) {
? let docs = [{}, {}, {}];
? let promises = docs.map((doc) => db.post(doc));
? let results = [];
? for (let promise of promises) {
? ? results.push(await promise);
? }
? console.log(results);
}//for循環(huán)中去請求網(wǎng)頁,若await操作成功灌危,會break退出康二;若失敗,會catch捕獲乍狐,進入下一輪循環(huán)const superagent = require('superagent');
const NUM_RETRIES = 3;
async function test() {
? let i;
? for(i = 0; i < NUM_RETRIES; ++i) {
? ? try {
? ? ? await superagent.get('http://google.com/this-throws-an-error');
? ? ? break;
? ? } catch(err) {}
? }
? console.log(i); // 3}
test();
7. async的錯誤處理
使用try...catch進行包裹赠摇,例如:
asyncfunction myFunction() {
? ? try {
? ? ? ? await somethingThatReturnsAPromise();
? ? } catch (err) {
? ? ? ? console.log(err);
? ? }
}
如果僅僅對一部分錯誤進行處理或者忽略,可以局部的進行包裹浅蚪,或者對單獨的promise進行catch藕帜,例如:
asyncfunction myFunction() {
? ? await somethingThatReturnsAPromise().catch((err)=> {
? ? ? ? ? ? console.log(err);
? ? })
}
async function myFunction() {
? ? try{
? ? ? ? await somethingThatReturnsAPromise();
? ? }
? ? catch(e){}
? ? await somethingElse();
}
Promise的錯誤處理,推薦用async + await來寫:
// 存值createData(title, successBack, errorBack) {
? ? // 使用key保存數(shù)據(jù)? ? storage.save({
? ? ? ? key: title,?
? ? ? ? data: 'true',
? ? }).then(successBack(), errorBack());
}
改寫為
//存值async createData1(title, successBack, errorBack) {
? ? try {
? ? ? ? // 使用key保存數(shù)據(jù)? ? ? ? await storage.save({
? ? ? ? ? ? key: title,?
? ? ? ? ? ? data: 'true',
? ? ? ? });
? ? ? ? successBack()
? ? } catch (e) {
? ? ? ? errorBack()
? ? }
}
形式上更加清晰一些惜傲。
8. async函數(shù)的實現(xiàn)原理
async函數(shù)就是將執(zhí)行器和Generator做為一個整體返回洽故。
asyncfunction fn(){}//等同于function fn(){
? ? returnspawn(function* (){
? ? })
}
spawn的實現(xiàn)
function spawn(genF) {
? ? /****
? ? * 返回的是一個promise
? ? */returnnewPromise(function(resolve, reject) {
? ? ? ? vargen=genF();//運行Generator這個方法;/***
? ? ? ? * 執(zhí)行下一步的方法
? ? ? ? * @param fn 一個調(diào)用Generator方法的next方法
? ? ? ? */function step(fn) {
? ? ? ? ? ? //如果有錯誤盗誊,則直接返回时甚,不執(zhí)行下面的awaittry {
? ? ? ? ? ? varnext=fn();
? ? ? ? ? ? }catch (e){
? ? ? ? ? ? return reject(e)
? ? ? ? ? ? }
? ? ? ? ? ? //如果下面沒有yield語句隘弊,即Generator的done是trueif(next.done){
? ? ? ? ? ? return resolve(next.value);
? ? ? ? ? ? }
? ? ? ? ? ? Promise.resolve(next.value).then((val)=>{
? ? ? ? ? ? ? ? step(function(){return gen.next(val) } )
? ? ? ? ? ? }).catch((e)=>{
? ? ? ? ? ? ? ? step(function(){returngen.throw(e) } )
? ? ? ? ? ? })
? ? ? ? }
? ? ? ? step(function () {
? ? ? ? ? ? return gen.next();
? ? ? ? })
? ? });
}