Callbacks, Promises and Async/Await

本文轉自做者Sandeep Dinesh的文章:Callbacks, Promises and Async/Awaitpromise

假設你有一個函數能夠在一段隨機的時間後打印一個字符串:dom

function printString(string){
  setTimeout(
    () => {
      console.log(string)
    }, 
    Math.floor(Math.random() * 100) + 1
  )
}

讓咱們嘗試按順序打印字母A,B,C:異步

function printAll(){
  printString("A")
  printString("B")
  printString("C")
}
printAll()

每次調用printAll時,您會注意到A,B和C以不一樣的隨機順序打印!async

這是由於這些函數是異步的。每一個函數按順序執行,但每一個函數都獨立於它本身的setTimeout。在開始以前,他們不會等待最後一個功能完成。函數

這很是煩人,因此讓咱們用回調修復它。code

Callbacks

回調是傳遞給另外一個函數的函數。第一個函數完成後,它將運行第二個函數。對象

function printString(string, callback){
  setTimeout(
    () => {
      console.log(string)
      callback()
    }, 
    Math.floor(Math.random() * 100) + 1
  )
}

你能夠看到,修改原始函數是很是容易的,可使用回調。ip

再次,讓咱們嘗試按順序打印字母A,B,C:字符串

function printAll(){
  printString("A", () => {
    printString("B", () => {
      printString("C", () => {})
    })
  })
}
printAll()

嗯,代碼如今很醜陋,但至少它有效!每次調用printAll時,都會獲得相同的結果。get

回調的問題是它建立了一個名爲「Callback Hell」的東西。基本上,你開始在函數內的函數內嵌套函數,而且開始變得很是難以閱讀代碼。

Promise

Promise嘗試修復這個嵌套問題。讓咱們改變咱們的功能來使用Promises

function printString(string){
  return new Promise((resolve, reject) => {
    setTimeout(
      () => {
       console.log(string)
       resolve()
      }, 
     Math.floor(Math.random() * 100) + 1
    )
  })
}

你能夠看到它看起來仍然很是類似。您將整個函數包裝在Promise中,而不是調用回調,而是調用resolve(若是出現錯誤則拒絕)。該函數返回此Promise對象。

再次,讓咱們嘗試按順序打印字母A,B,C:

function printAll(){
  printString("A")
  .then(() => {
    return printString("B")
  })
  .then(() => {
    return printString("C")
  })
}
printAll()

這被稱爲承諾鏈。您能夠看到代碼返回函數的結果(將是Promise),並將其發送到鏈中的下一個函數。

代碼再也不嵌套,但看起來仍然很混亂!

經過使用箭頭函數的功能,咱們能夠刪除「包裝器」功能。代碼變得更清晰,但仍然有不少沒必要要的括號:

function printAll(){
  printString("A")
  .then(() => printString("B"))
  .then(() => printString("C"))
}
printAll()

Await

Await基本上是Promises的語法糖。它使您的異步代碼看起來更像是同步/過程代碼,人類更容易理解。

該PRINTSTRING功能不自許的版本在全部改變。

再次,讓咱們嘗試按順序打印字母A,B,C:

async function printAll(){
  await printString("A")
  await printString("B")
  await printString("C")
}
printAll()

是啊...。好多了!

您可能會注意到咱們對包裝函數printAll使用「async」關鍵字。這讓咱們的JavaScript知道咱們正在使用async / await語法,若是你想使用Await,這是必要的。這意味着你不能在全球範圍內使用Await; 它老是須要一個包裝函數。大多數JavaScript代碼都在函數內部運行,所以這不是什麼大問題。

等等,這裏還有更多哦

該PRINTSTRING函數不返回任何東西,是獨立的,全部咱們關心的是順序。可是,若是您想獲取第一個函數的輸出,在第二個函數中執行某些操做,而後將其傳遞給第三個函數,該怎麼辦?

咱們不是每次都打印字符串,而是建立一個鏈接字符串並傳遞它的函數。

Callbacks

這裏是回調樣式:

function addString(previous, current, callback){
  setTimeout(
    () => {
      callback((previous + ' ' + current))
    }, 
    Math.floor(Math.random() * 100) + 1
  )
}

爲了使用它:

function addAll(){
  addString('', 'A', result => {
    addString(result, 'B', result => {
      addString(result, 'C', result => {
       console.log(result) // Prints out " A B C"
      })
    })
  })
}
addAll()

不太好。

Promises

這是Promise風格:

function addString(previous, current){
  return new Promise((resolve, reject) => {
    setTimeout(
      () => {
        resolve(previous + ' ' + current)
      }, 
      Math.floor(Math.random() * 100) + 1
    )
  })
}

爲了使用它:

function addAll(){  
  addString('', 'A')
  .then(result => {
    return addString(result, 'B')
  })
  .then(result => {
    return addString(result, 'C')
  })
  .then(result => {
    console.log(result) // Prints out " A B C"
  })
}
addAll()

使用箭頭函數意味着咱們可使代碼更好一些:

function addAll(){  
  addString('', 'A')
  .then(result => addString(result, 'B'))
  .then(result => addString(result, 'C'))
  .then(result => {
    console.log(result) // Prints out " A B C"
  })
}
addAll()

這確定更具可讀性,特別是若是你向鏈添加更多,但仍然是一堆括號。

Await

該功能與Promise版本保持一致。

而且爲了使用它:

async function addAll(){
  let toPrint = ''
  toPrint = await addString(toPrint, 'A')
  toPrint = await addString(toPrint, 'B')
  toPrint = await addString(toPrint, 'C')
  console.log(toPrint) // Prints out " A B C"
}
addAll()

Yeah. SO MUCH BETTER~

相關文章
相關標籤/搜索