async-for-js

介紹關於js開發中所涉及的主流異步編程解決方案

repo: async-for-js

例子

插入3個div元素,其中第二個div元素使用setTimeout模擬異步操作,理想的插入順序爲div1 div2 div3,但這裏的代碼的插入順序爲div1 div3 div2。

// async way
function _async() {
  document.body.appendChild(div1)

  setTimeout(function () {
    document.body.appendChild(div2)
  }, 2000)

  document.body.appendChild(div3)
}

_async()

Callback

最常用的方法是利用callback(回調函數)的方式,因爲js中函數也是作爲對象存在的,因此可以被當做參數傳入另一個函數中,只需要在異步操作執行代碼後調用回調函數即可。

但是使用回調函數有很明顯的侷限性,一方面體現在需要自己對異步操作進行控制,另一方面還很容易陷入”回調地獄”。

// use plain callback to sync
function _callback(cb) {
  document.body.appendChild(div1)

  setTimeout(function () {
    document.body.appendChild(div2)
    cb('done')
  }, 2000)

  return 'done'
}

_callback(function () {
  document.body.appendChild(div3)
})

Promise

因爲回調地獄的問題,後來聰明的人使用將回調延遲執行的思想,從而發明了promise庫,調用者可以根據異步流程隨心所欲的resolve或reject某個值給之後的操作,從而解決了毀掉地獄的問題。

不過使用promise仍然有問題,就是當代碼邏輯很長的時候,總需要帶着大片大片的then方法,可讀性仍然不夠清晰。

// use promise to sync
function _promise() {
  document.body.appendChild(div1)

  return new Promise(res => {
    setTimeout(function () {
      document.body.appendChild(div2)
      res('done')
    }, 2000)
  })
}

_promise().then(data => {
  console.log(data)
  document.body.appendChild(div3)
})

Generate

後來promise加入了es6標準,同時推出了新的異步解決方案,叫做generate函數,大體講是提供了一個具有狀態機功能的函數,每次執行會停止在實現者聲明的某個狀態,下次調用會繼續從這個狀態開始執行。

generate的出現,使必須依靠callback實現異步操作的代碼風格,可以使用同步代碼風格實現,是一顆非常甜的語法糖。

但是它仍有有一些缺點,就是它作爲狀態機,無法自執行,必須藉助實現一個run函數或使用第三方庫(如co)。

// use generate to sync
function* _generate() {
  document.body.appendChild(div1)

  yield function (cb) {
    setTimeout(function () {
      document.body.appendChild(div2)
      cb()
    }, 2000)
  }

  document.body.appendChild(div3)

  return 'done'
}

function run(fn) {
  var gen = fn()

  function next(data) {
    var result = gen.next(data)

    console.log(result.value)

    if (result.done) return

    result.value(next)
  }

  next()
}

run(_generate)

Async/await

爲了解決generate的缺點,es7很快發佈了繼generate更強大的一個東西,叫做async函數。簡單說,它並沒有什麼新特性,把它看做是可以自執行的generate函數即可,其中的await的操作符可以看做是yield操作符的翻版。

// use async/await and promise to sync
const fn = function () {
  return new Promise(res => {
    setTimeout(function () {
      res(document.body.appendChild(div2))
    }, 2000)
  })
}

async function _await () {
  document.body.appendChild(div1)
  const f = await fn()
  console.log(f)
  document.body.appendChild(div3)
}

_await()

Observable

最近很火的rxjs也快成用來解決這個問題,詳細的介紹可以去它的官網瞭解。

// use rxjs and callback to sync
const _callbackObservable = Observable.bindCallback(_callback)
const result = _callbackObservable()

// result.subscribe(x => {
//   document.body.appendChild(div3)
//   console.log(x)
// })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章