ES2017 —— async await原理

混沌傳奇發表於2019-05-26

Generator不熟悉的人,可以先看下Generator的文件:developer.mozilla.org/zh-CN/docs/…

前言

在ES2017之前,我們要想把一段非同步邏輯程式設計按照同步程式碼的方式來編寫,需要依賴Generator,比如:

function getJSONFile() {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve({
        name: '大漠傳奇',
        age: 23,
      })
    }, 1000)
  })
}

function getHobby() {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve({
        hobby: ['爬山','旅遊','聽歌','看電影','看小說','打遊戲'],
      })
    }, 1000)
  })
}

function* gen(i) {
  let me = yield getJSONFile();
  console.log('me:', me);
  let hobby = yield getHobby();
  console.log('hobby:', hobby)
}

function co(genFn) {
  let gen = genFn();
  let res = gen.next()
  let run = (gen, res) => {
    if(!res.done){
      if(res.value&&(res.value.__proto__.constructor === Promise)){
        res.value.then((data) => {
          let res2 = gen.next(data)
          run(gen, res2);
        })
      }else{
        let res3 = gen.next(res.value);
        run(gen, res3);
      }
    }
    
  }
  run(gen, res);
}

co(gen)
複製程式碼

但是,在ES2017提供了async && await之後,我們上面這一坨程式碼,就可以用更加優雅的方式來編寫了。比如:

// async await
async function likeGen() {
  let me = await getJSONFile();
  console.log('me:', me);
  let hobby = await getHobby();
  console.log('hobby:', hobby);
  return 'end';
}

likeGen().then(res => {
  console.log('res:', res);
})
複製程式碼

兩相對比,用async+await來編寫,最明顯的變化就是少了一個co函式,程式碼量少了一些。其他的程式碼看起來跟用generator實現相差不大。 那麼為什麼ES2017提供的async && await來編寫同步程式碼時,程式碼量少了一些,並且更方便呢?

具體原因我們下面來介紹,我們先看下async && await的功能簡介。

async && await功能簡介

async

async function 宣告用於定義一個返回 AsyncFunction 物件的非同步函式。非同步函式是指通過事件迴圈非同步執行的函式,它會通過一個隱式的 Promise 返回其結果。但是如果你的程式碼使用了非同步函式,它的語法和結構會更像是標準的同步函式。

當呼叫一個 async 函式時,會返回一個 Promise 物件。當這個 async 函式返回一個值時,Promise 的 resolve 方法會負責傳遞這個值;當 async 函式丟擲異常時,Promise 的 reject 方法也會傳遞這個異常值。

async 函式中可能會有 await 表示式,這會使 async 函式暫停執行,等待 Promise 的結果出來,然後恢復async函式的執行並返回解析值(resolved)。

注意, await 關鍵字僅僅在 async function中有效。如果在 async function函式體外使用 await ,你只會得到一個語法錯誤(SyntaxError)。

await

await 操作符用於等待一個Promise 物件。它只能在非同步函式 async function 中使用。

await 表示式會暫停當前 async function 的執行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回撥的resolve函式引數作為 await 表示式的值,繼續執行 async function。

若 Promise 處理異常(rejected),await 表示式會把 Promise 的異常原因丟擲。

另外,如果 await 操作符後的表示式的值不是一個 Promise,則返回該值本身。

async+await VS generator

ES2017引入async和await目的就是簡化用generator來寫同步程式碼時的繁瑣,降低用js來寫同步程式碼的複雜程度,降低程式碼量,讓語法更加簡單。

你可以把async和await理解為前言中前半部分用generator實現同步程式碼的上層簡易語法糖。也就是瀏覽器幫我們在js直譯器中實現了底層部分,我們用async和await時,js直譯器就會理解為generator+co函式(前言中實現的generator自動執行函式)。

這樣,我們就不用去關注generator以及如何讓generator自動執行的問題了,要想寫同步程式碼,只需要定義一個async函式,在async函式中用await來等待非同步程式碼的執行成功。即簡單又方便,程式碼量還少。

相關文章