循環與異步操做(Promise/async/await)

前言

Js中有使人頭疼的地獄回調的問題,因而有了Promise,有了鏈式調用,基於Promise也出現了各類異步解決方案就出現了,其中最好的我的感受應該是async/await的解決方案了,一直以來都是模模糊糊的使用,直到碰見了循環與異步的問題,因而花時間查閱資料從1分懂到了4分懂.javascript

關於Promise

網上關於Promise的文章有不少,也講得很好,這裏也簡單的記錄一下.java

基本概念

Promise不只經過鏈式調用解決了回調嵌套的問題,還解決了回調函數中使用 return 和 throw 的問題,最重要的是它在異步解決方案中的使用.
Promise一共擁有pending/resloved/rejected(進行中/已完成/已失敗)這三個狀態,resloved/rejected狀態能夠由pendding狀態變化獲得,並且這個狀態一旦變化就不會再更改.數組

經常使用API

  • Promise.all() // 多個Promise並行進行,當全部Promise reslove時reslove.當有一個Promise reject時reject
  • Promise.race() // 與Promise.all相似,差異在當第一個Promise reslove時reslove.
  • Promise.then() // 用於鏈式調用
  • Promise.catch() // 捕獲reject

關於異步的栗子

function sleep(dely) {
  return new Promise((reslove) => {
    setTimeout(() => {
      console.log(`dely ${dely}s`)
      reslove();
    }, dely * 1000);
  })
}

(async () => {
  console.log('before sleep')
  await sleep(2);
  console.log('after sleep')
})()

/** console
* before sleep
* dely 2s
* after sleep
*/

關於 async/await

這個是ES7中的異步解決方案,在Node v8.0.0及其以上已經原生支持,在低版本的Node中須要開啓--harmony-async-await 選項異步

如何使用

  • async 表示這是一個async函數,await只能用在這個函數裏面。
  • await 表示在這裏等待Promise返回結果了,再繼續執行。
  • await 後面跟着的應該是一個Promise對象
    栗子能夠參考上一個栗子,使用async/await不只語義明確,操做簡單,並且還能夠同步捕獲錯誤哦.

循環與異步操做

例若有以下設定: 每一個人的碎覺時間都是固定值,若是有多我的碎覺,則後一我的的碎覺時間是前面碎覺時間的累積和,寫一個函數在傳入name數組時能實現以上描述,話很少說上代碼async

const users = { // 定義3我的,且每一個人睡覺時間是1
  leo: {
    name: 'leo',
    sleep: 1
  },
  mick: {
    name: 'mick',
    sleep: 1
  },
  jack: {
    name: 'jack',
    sleep: 1
  }
}

function sleep(name, sleep) { // 碎覺函數,包括打印當前碎覺時間,和累加未碎覺人的時間
  return new Promise((reslove) => {
    setTimeout(() => {
      console.log(`${name} sleep ${sleep}`);
      Object.keys(users).forEach(u => {
        if (name !== u) {
          users[u].sleep += sleep;
        }
      })
      reslove();
    }, sleep * 1000);
  })
}

我第一次寫出來是這樣的函數

(function consoleSleep(names) {
  return Promise.all(names.map(async n => {
    await sleep(users[n].name, users[n].sleep)
  }))
}(['leo', 'mick', 'jack']))
/** console
* leo sleep 1
* mick sleep 1
* jack sleep 1
*/

當場我就很懵逼,把name 數組拿來循環,而後循環中等待當前sleep時的各類異步操做,但結果讓人很憂桑,在分析問題後,發現這是一個串行執行異步操做的問題,
好吧,Promise.all()是並行進行的,但是在去掉Promise.all後結果任然沒有改變,通過各類查資料後發現是map的問題(笑哭)(map和forEach都是並行迭代的),好吧再改代碼學習

(async function consoleSleep(names) {
  for(let i = 0; i < names.length; i++) {
    await sleep(users[names[i]].name, users[names[i]].sleep)
  }
}(['leo', 'mick', 'jack']))

/** console
* leo sleep 1
* mick sleep 2
* jack sleep 4
*/

終於實現了功能,大功告成code

小結

Promise不只解決了回調嵌套還提供了異步解決的方式,可使用Promise封裝異步操做而後使用async/await來把異步操做變成同步的,在循環中使用Promise時要注意並行和串行兩種方式帶來的不一樣操做結果,固然其它庫也提供了相應封裝的方法(例如bluebird有mapSeries能夠直接迭代串行執行多個Promise).
ps: 學習了,map/forEach是並行的迭代,而不是同步的循環,切記!切記!切記!對象

相關文章
相關標籤/搜索