async的基本用法

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

相關文章
相關標籤/搜索