JavaScript學習之ES6-async\await 異步

async/await

ES2017 提出的async 函數,終於讓 JavaScript 對於異步操作有了終極解決方案。No more callback hell。

  • async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。
    想較於 Generator,Async 函數的改進在於下面四點:

  • 內置執行器。Generator 函數的執行必須依靠執行器,而 Aysnc 函數自帶執行器,調用方式跟普通函數的調用一樣;

  • 更好的語義。async 和 await 相較於 * 和 yield 更加語義化;

  • 更廣的適用性。co 模塊約定,yield 命令後面只能是 Thunk 函數或 Promise對象。而 async 函數的 await命令後面則可以是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同於同步操作);

  • 返回值是 Promise。async 函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator對象方便,可以直接使用 then() 方法進行調用。

//promise實例
const makeRequest = ()=>{
    return getJson()
    .then(data=>{
        if(data.needsAnotherRequest){
            return makeAnotherRequest(data)
            .then(moreData=>{
                console.log(moreData);
                return moreData;
            })
        }else{
            console.log(data);
            return data;
        }
    })
}

//async/await
const makeRequest = async()=>{
    const data = await getJson()
    if(data.needsAnotheRRequest){
        const moreData = await makeAnotherRequest(data);
        console.log(moreData);
        return moreData;
    }else{
        console.log(data);
        return data;
    }
}

阮一峯大神的ES6入門

返回thenable對象

class sleep{
    constructor(timeout){
        this.timeout = timeout;
    }
    then(resolve,reject){
        const startTime = Date.now();
        setTimeout(
            ()=>resolve(Date.now()-startTime),
        this.timeout
        );
    }
}
(async ()=>{
    const sleep = await new Sleep(1000);
    console.log(sleepTime);
})();

javascript 休眠

function sleep(interval) {
  return new Promise(resolve => {
    setTimeout(resolve, interval);
  })
}

// 用法
async function one2FiveInAsync() {
  for(let i = 1; i <= 5; i++) {
    console.log(i);
    await sleep(1000);
  }
}

one2FiveInAsync();

任何一個await語句後面的 Promise 對象變爲reject狀態,那麼整個async函數都會中斷執行。

async function f() {
  await Promise.reject('出錯了');
  await Promise.resolve('hello world'); // 不會執行
}

錯誤處理

如果await後面的異步操作出錯,那麼等同於async函數返回的 Promise 對象被reject。

async function f(){
    await new Promise(function(resolve,reject){
        throw new Error("出錯了");
    });
}
f()
.then(v=>console.log(v))
.catch(e=>console.log(e))
//Error 出錯了

防止出錯的方法,也是將其放在try…catch代碼塊之中。

async function f(){
    try{
        await new Promiseh(function(resolve,reject){
            throw new Error('出錯了');
        });
    }catch(e){

    }
    return await('hello world');
}

實現原理

將Generator函數和自動執行器包裝在一個函數中

async function fn(args){
    //something to do
}

function fn(args){
    return spawn(function* (){
        //
    });
}

spawn 是自動執行器 簡單實現如下:

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

手寫Async

function asyncToGenerator(generatorFunc) {
  // 返回的是一個新的函數
  return function() {
  
    // 先調用generator函數 生成迭代器
    // 對應 var gen = testG()
    const gen = generatorFunc.apply(this, arguments)

    // 返回一個promise 因爲外部是用.then的方式 或者await的方式去使用這個函數的返回值的
    // var test = asyncToGenerator(testG)
    // test().then(res => console.log(res))
    return new Promise((resolve, reject) => {
      // 內部定義一個step函數 用來一步一步的跨過yield的阻礙
      // key有next和throw兩種取值,分別對應了gen的next和throw方法
      // arg參數則是用來把promise resolve出來的值交給下一個yield
      function step(key, arg) {
        let generatorResult
        // 這個方法需要包裹在try catch中
        // 如果報錯了 就把promise給reject掉 外部通過.catch可以獲取到錯誤
        try {
          generatorResult = gen[key](arg)
        } catch (error) {
          return reject(error)
        }

        // gen.next() 得到的結果是一個 { value, done } 的結構
        const { value, done } = generatorResult

        if (done) {
          // 如果已經完成了 就直接resolve這個promise
          // 這個done是在最後一次調用next後纔會爲true
          // 以本文的例子來說 此時的結果是 { done: true, value: 'success' }
          // 這個value也就是generator函數最後的返回值
          return resolve(value)
        } else {
          // 除了最後結束的時候外,每次調用gen.next()
          // 其實是返回 { value: Promise, done: false } 的結構,
          // 這裏要注意的是Promise.resolve可以接受一個promise爲參數
          // 並且這個promise參數被resolve的時候,這個then纔會被調用
          return Promise.resolve(
            // 這個value對應的是yield後面的promise
            value
          ).then(
            // value這個promise被resolve的時候,就會執行next
            // 並且只要done不是true的時候 就會遞歸的往下解開promise
            // 對應gen.next().value.then(value => {
            //    gen.next(value).value.then(value2 => {
            //       gen.next()
            //
            //      // 此時done爲true了 整個promise被resolve了 
            //      // 最外部的test().then(res => console.log(res))的then就開始執行了
            //    })
            // })
            function onResolve(val) {
              step("next", val)
            },
            // 如果promise被reject了 就再次進入step函數
            // 不同的是,這次的try catch中調用的是gen.throw(err)
            // 那麼自然就被catch到 然後把promise給reject掉啦
            function onReject(err) {
              step("throw", err)
            },
          )
        }
      }
      step("next")
    })
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章