簡單實(shí)現(xiàn)Promise,await,async

我們處理異步的方式,從開始的回調(diào),到Promise,再到現(xiàn)在的async,await,變得越來越方便,直觀了。但是知其然要知其所以然,所以我們一步步來分析他們是如何實(shí)現(xiàn)的(需要知道Promise的使用方法,await async的使用方法,以及generator的功能)。

Promise

function process() {
  setTimeout(() => {
    console.log("timeout")
  }, 1000)
}

如果我們想要執(zhí)行上面的函數(shù),并且在計(jì)時(shí)結(jié)束后的回調(diào)中執(zhí)行某些動(dòng)作,應(yīng)該怎么辦呢?
我們可以使用一個(gè)回調(diào):

function process(callback) {
  setTimeout(() => {
    console.log("timeout")
     callback()
  }, 1000)
}

process(function doSomething() {
  console.log("do something")
})

這是我們正常的回調(diào)方式,但是這樣的方式不夠靈活,而且當(dāng)需要嵌套回調(diào)的時(shí)候,往往就寫成了callback hell。Promise其實(shí)就是對(duì)上面的process進(jìn)行一層包裝。
下面實(shí)現(xiàn)一個(gè)最簡單的Promise

function Promise(fn) {
  const callbacks = [];

  this.then(calback) {
    callbacks.push(callback)
  }

  function resolve(value) {
    callbacks.forEach(callback => callback && callback(value))
  }

  fn(resolve)
  return this
} 

從代碼可以看到,then函數(shù)其實(shí)就是把傳入的函數(shù)放到一個(gè)回調(diào)數(shù)組里,這也讓原本單個(gè)的回調(diào)變成支持許多回調(diào)。resolve函數(shù)就是傳回我們將要執(zhí)行的函數(shù)中的回調(diào),他會(huì)調(diào)用每一個(gè)then里面添加的回調(diào)。最后,執(zhí)行傳入的函數(shù),并且返回this

可以向正常的Promise一樣使用:

var p = Promise(function process(resolve) {
  setTimeout(() => {
    console.log("timeout")
     resolve()
  }, 1000)
})

p
.then(() => console.log("timeout!!"))

寫到這里,也就明白resolve,reject是怎么實(shí)現(xiàn)的了。

await async

generator

await 和 async,需要在generator的基礎(chǔ)上來實(shí)現(xiàn)。generator可以實(shí)現(xiàn)控制函數(shù)間執(zhí)行順序的功能??赡苷f的有些抽象,但是理解了之后就感覺不難了。

function* generateValue() {
  console.log("first")
  yield "hello"
  console.log("second")
  return "world"
}

var g = generateValue()
console.log(g.next())
console.log(g.next())

// 輸出:
// first
// { value: 'hello', done: false }
// second
// { value: 'world', done: true }

這是一個(gè)生成器函數(shù),在其他語言也有類似的語法。在JavaScript中需要在函數(shù)名稱前帶上*才能使用yield來使用生成器。

generator函數(shù)的特點(diǎn)是,每次執(zhí)行到y(tǒng)ield時(shí),執(zhí)行權(quán)交到調(diào)用它的地方,等待下一次調(diào)用next()之后才會(huì)繼續(xù)執(zhí)行。

await async

那么generator與await和async是什么關(guān)系呢?

同樣的,我們先看問題
(axios是一個(gè)強(qiáng)大的HTTP client,可以同時(shí)用于web和node)

axios.get("https://www.github.com")
.then(result => console.log(result))

代碼中,如果我們想要正常的輸出結(jié)果,就可以在一個(gè)then的調(diào)用中來執(zhí)行l(wèi)og。但是怎么樣不使用Promise,而用更加自然的方式實(shí)現(xiàn)呢

function* syncDemo() {
  const result = yield axios.get("https://www.github.com")
  console.log('after get result', result)
}

我們利用yield,在上面的代碼中,如果執(zhí)行syncDemo(),函數(shù)執(zhí)行到y(tǒng)ield時(shí)會(huì)把代碼的控制權(quán)轉(zhuǎn)到調(diào)用函數(shù)的地方,于是參考generator的使用方法:

var g = syncDemo()
var p = g.next().value
p.then(result => g.next(result))

在代碼中g.next(result)中的result會(huì)傳回generator中賦給result,然后繼續(xù)執(zhí)行。
這就到了關(guān)鍵的地方,可以看到,利用generator的這個(gè)特性,我們已經(jīng)實(shí)現(xiàn)了一個(gè)可以順序執(zhí)行的異步函數(shù),但是在調(diào)用這個(gè)函數(shù)的時(shí)候,需要控制執(zhí)行的過程。那么是不是可以寫一個(gè)函數(shù)專門來自動(dòng)執(zhí)行這個(gè)步驟呢?

function runGenerator(fn) {
  var g = fn()
  g.next().value.then(result => {
    g.next(result)
  })
}

上面就是一個(gè)非常簡單的執(zhí)行函數(shù)。
調(diào)用:

runGenerator(syncDemo) 

syncDemo就自動(dòng)執(zhí)行了。

而JavaScript的async,await做的也是同樣的事情。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容