1. async函數的基本形式html
//函數聲明 async function foo() {} //函數表達式 const foo = async function () {}; //對象的方法 let obj = { async foo() {} }; obj.foo().then(...) //Class 的方法 class Storage { constructor() { this.cachePromise = caches.open('avatars'); } async getAvatar(name) { const cache = await this.cachePromise; return cache.match(`/avatars/${name}.jpg`); } } const storage = new Storage(); storage.getAvatar('jake').then(…); //箭頭函數 const foo = async () => {};
2. async函數的返回值老是一個Promiseshell
不管async函數有無await操做,其老是返回一個Promise。express
1. 沒有顯式return,至關於return Promise.resolve(undefined);
2. return非Promise的數據data,至關於return Promise.resolve(data);
3. return Promise, 會獲得Promise對象自己segmentfault
async老是返回Promise,所以,其後面能夠直接調用then方法,
函數內部return返回的值,會成爲then回調函數的參數
函數內部拋出的錯誤,會被then的第二個函數或catch方法捕獲到promise
//正常返回值 async function f(){ retrun 'hello world'; } f().then(v => console.log(v));//hello world //拋出錯誤 async function f(){ throw new Error('出錯了'); } f().then( v => console.log(v), e => console.log(e) //Error: 出錯了 )
3. await操做符的值異步
[rv] = await expression(expression能夠是任何值,一般是一個promise)
expression是Promise,rv等於Promise兌現的值,若Promise被拒絕,則拋出異常,由catch捕獲
expression是非Promise,會被轉換爲當即resolve的Promise,rv等於expressionasync
await操做只能用在async函數中,不然會報錯。函數
4. async就是generator和promise的語法糖post
//generator寫法 var gen = function* () { var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; //async寫法 var asyncReadFile = async function () { var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
async就是將 generator的 * 換成 async,將 yield 換成 await。ui
5. async對generator的改進
1. 內置執行器
Generator必須依靠執行器調用next方法來自動執行,例如co模塊。而async函數自帶執行器,能夠自動執行。
2. 更好的語義
async和await分別表示異步和等待,語義更加明確
3. 適用性更強
co模塊後面只能是Thunk函數或Promise對象,而await後面能夠是Promise或基本數據類型(如:數字,字符串,布爾等)
4. 返回Promise,能夠繼續操做
async函數老是返回一個Promise對象,能夠對其進行then調用,繼續操做後面的數據,所以,
async函數徹底能夠看做是多個Promise合成一個Promise對象,而await命令就是內部的then調用。
6. async內部的並行調用
async配合await都是串行調用,可是如有並行調用,則應按照如下方式來寫:
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這種寫法有缺陷,一個調用報錯,會終止,這個不太符合並行調用的初衷。
3. 使用多個async函數
實際上,一個async函數內部包含的調用應該是強相關的,沒有依賴關係的函數調用不該該放在一個async函數中,分開來邏輯更清晰。
4. 並行執行的一些寫法
1. 不能再內部非async function中使用await
async function dbFuc(db) { let docs = [{}, {}, {}]; // 報錯,forEach的function是非async,不能使用await docs.forEach(function (doc) { await db.post(doc); }); } //這裏不須要 async function dbFuc(db) { let docs = [{}, {}, {}]; // 可能獲得錯誤結果,這樣調用也不能獲得正確的結果 docs.forEach(async function (doc) { await db.post(doc); }); }
2. 循環調用await可使用for循環或for of循環
//for of async function dbFuc(db) { let docs = [{}, {}, {}]; for (let doc of docs) { await db.post(doc); } } //map + Promise.all async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = await Promise.all(promises); console.log(results); } //map + for of async function 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循環中去請求網頁,若await操做成功,會break退出;若失敗,會catch捕獲,進入下一輪循環 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進行包裹,例如:
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } }
若是僅僅對一部分錯誤進行處理或者忽略,能夠局部的進行包裹,或者對單獨的promise進行catch,例如:
async function 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保存數據 storage.save({ key: title, data: 'true', }).then(successBack(), errorBack()); }
改寫爲
//存值 async createData1(title, successBack, errorBack) { try { // 使用key保存數據 await storage.save({ key: title, data: 'true', }); successBack() } catch (e) { errorBack() } }
形式上更加清晰一些。
8. async函數的實現原理
async函數就是將執行器和Generator作爲一個總體返回。
async function fn(){} //等同於 function fn(){ return spawn(function* (){ }) }
spawn的實現
function spawn(genF) { /**** * 返回的是一個promise */ return new Promise(function(resolve, reject) { var gen=genF(); //運行Generator這個方法; /*** * 執行下一步的方法 * @param fn 一個調用Generator方法的next方法 */ function step(fn) { //若是有錯誤,則直接返回,不執行下面的await try { var next=fn(); }catch (e){ return reject(e) } //若是下面沒有yield語句,即Generator的done是true if(next.done){ return resolve(next.value); } Promise.resolve(next.value).then((val)=>{ step(function(){ return gen.next(val) } ) }).catch((e)=>{ step(function(){ return gen.throw(e) } ) }) } step(function () { return gen.next(); }) }); }
參考:https://segmentfault.com/a/1190000008677697 https://juejin.im/post/5b56837c6fb9a04fe0181555 https://www.cnblogs.com/goloving/p/8013119.html https://blog.csdn.net/u011272795/article/details/80197481