20170702-非同步程式設計之 async await

weixin_34127717發表於2017-07-02

async await

  • async await是非同步程式設計的另一種解決方案

  • async函式是對Generator函式的改進

async的基本用法

async函式

async函式返回一個 Promise 例項,可以使用then方法(為返回的Promise例項)新增回撥函式。當函式執行的時候,一旦遇到await就會先返回,等到非同步操作完成,再接著執行函式體內後面的語句。

  • 例 1:

clipboard.png

  • 上面程式碼是一個獲取股票報價的函式,函式前面的async關鍵字,表明該函式內部有非同步操作。呼叫該函式時,會立即返回一個Promise例項。

  • async函式內部return語句返回的值,會成為then方法回撥函式的引數。

Promise物件的狀態變化

async函式返回的 Promise物件,必須等到內部所有await命令後面的 Promise 物件執行完,才會發生狀態改變,除非遇到return語句或者丟擲錯誤。也就是說,只有async函式內部的非同步操作執行完,才會執行then方法指定的回撥函式。

await命令的基本用法

正常情況下,await命令後面是一個Promise例項,如果不是,會被轉成一個立即resolvePromise例項。

async function f() {
  return await 123;
}

f().then(v => console.log(v))

等價於

async function f() {
    return await new Promise(function(resolve){
        resolve(123)
    })
}
f().then(v => console.log(v))

等價於

async function f() {
    return await Promise.resolve('123')
}
f().then(v => console.log(v))
  • await語句的返回值是await命令後面Promise例項的結果(非同步處理的結果)

function getResult() {
  return new Promise((resolve) => {
    resolve('result: 1000')  // resolve()方法的引數就是非同步處理的結果
  });
}

async function asyncPrint() {
  const result = await getResult()  // 將非同步處理的結果賦值給result
  return result
}

asyncPrint().then( (result) => { console.log(result) } ) //'result: 1000'

異常處理

如果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 Promise(function (resolve, reject) {
      throw new Error('出錯了');
    });
  } catch(e) {
  }
  return await('hello world');
}

使用注意

  • 如果await後面的非同步操作出錯,那麼等同於async函式返回的 Promise 物件被reject,所以最好把await命令放在try...catch程式碼塊中。

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一種寫法

async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err);
  });
}
  • 多個await命令後面的非同步操作,如果不存在繼發關係,最好讓它們同時觸發。

let foo = await getFoo();
let bar = await getBar();

上面程式碼中,getFoogetBar是兩個獨立的非同步操作(即互不依賴),被寫成繼發關係(只有執行完getFoo操作,才能去執行getBar操作)。這樣比較耗時,因為只有getFoo完成以後,才會執行getBar,完全可以讓它們同時觸發。

解釋:這裡的getFoogetBar方法會返回兩個Promise例項(假設是發起Ajax請求,請求foo和bar的內容),只有執行了方法,對應的操作才會執行,如果寫成上面的形式,就會導致執行完getFoo的操作後(等待收到伺服器的響應後),才能執行getBar的操作,這樣就成了同步,比較耗時,因此可以將上面的寫法修改,使得在等待getFoo執行完的時間內(在等待伺服器響應的期間)去執行執行getBar

記住:當函式執行的時候,一旦遇到await就會先返回,等到非同步操作完成,再接著執行函式體內後面的語句。

// 寫法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 寫法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

上面兩種寫法,getFoogetBar都是同時觸發,這樣就會縮短程式的執行時間。

參考資料

ECMAScript 6 入門

相關文章