js循環語句中的async與await

前言

入冬了,回想起 2016 年的那個寒冬,我遭遇了裁人的風波。給我敲響了警鐘,基礎知識不能忘,時常要複習複習,才能應對一些突如其來的事情,今天篇文章也是由於以前被考到,記憶模糊回答的不是很肯定,因此提筆再次記錄一番。javascript

正文

ES7 引入了 async、await 方法,來處理異步操做。關於這倆方法的使用,不贅述,網上的文章多如牛毛,又寫一篇入門,未免廢話連篇。今天要分享的內容是在js的一些循環方法中,async 和 await 的表現是如何的。java

for 循環

在 for 循環中,async 和 await 表現的仍是使人滿意。如今假設一個場景,設置一個變量,爸媽我三我的的體重,求和數組

const family = {
  "dad": 150,
  "mom": 100,
  "son": 200
}
const familyToGet = ["dad", "mom", "son"]


// 寫一個sleep方法
const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
}
// 假設獲取體重數據的過程是一個請求接口的異步過程
const getFamilyWeight = person => {
  return sleep(2000).then(() => family[person])
}

const loop_for = async () => {
  console.log('start')
  let result = 0
  for(let i = 0; i < familyToGet.length; i++) {
    const person = familyToGet[i]
    const weight = await getFamilyWeight(person)
    result += weight
  }
  console.log('result:', result)
  console.log('end')
}

loop_for()
// 運行結果
// start
// 150
// 100
// 200
// result: 450
// end
複製代碼

按照上述代碼的打印結果分析,for 循環內的代碼,每一次都會等當前的 getFamilyWeight 方法返回了內容以後,纔會繼續下一個循環,循環所有結束的時候,纔會跑 for 循環下面的代碼,這是咱們想要的結果,nice。promise

forEach 循環

在 forEach 中,async 和 await 表現的差強人意,具體緣由,還應歸結於 forEach 中的回調函數,具體看看代碼的表現。markdown

const family = {
  "dad": 150,
  "mom": 100,
  "son": 200
}
const familyToGet = ["dad", "mom", "son"]


// 寫一個sleep方法
const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
}
// 假設獲取體重數據的過程是一個請求接口的異步過程
const getFamilyWeight = person => {
  return sleep(2000).then(() => family[person])
}

const loop_forEach = () => {
  console.log('start')
  let result = 0
  // 在回調函數中,異步是很差控制的
  familyToGet.forEach(async person => {
    const num = await getFamilyWeight(person)
    console.log(num)
    result += num
  })
  console.log('result', result)
  console.log('end')
}

loop_forEach()

// 運行結果
// start
// result 0
// end
// 150
// 100
// 200
複製代碼

從運行結果中,能夠看出,代碼沒有等待forEach方法執行結束,而是直接繼續日後執行了。回調函數傳進去以後,2秒鐘以後纔會返回數據,這不是咱們想要的結果,我如今是但願它能順序的執行。異步

那麼咱們分析一下 forEach 內部大體的原理async

Array.prototype.forEach = function(cb) {
  // this 爲當前調用該函數的變量
  for(let i=0; i < this.length; i++) {
      cb(this[i], i);
  }
}
複製代碼

且看上面的簡易 forEach 重寫,其實內部也是一個 for 循環,那麼爲何不能表現的和 for 循環同樣的運行結果呢?函數

咱們的回調函數進入 forEach 以後,又被單獨的以 cb() 這種形式執行了一次,至關於在內部又建立了一個 async 、await 形式的方法,當 forEach 函數執行完的時候,至關於建立了 3 個方法,這 3 個方法須要等待2 秒鐘,數據纔會返回,而 forEach 外面的代碼是不會等這 3 個函數返回內容再執行,而是獨斷獨行的管本身走了,因此纔會形成這樣的局面。返回問題的根本,我如今的需求是想要代碼能順序執行,且最後能算出一家人體重之和,下面咱們來修改一下代碼。oop

const family = {
  "dad": 150,
  "mom": 100,
  "son": 200
}
const familyToGet = ["dad", "mom", "son"]


// 寫一個sleep方法
const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
}
// 假設獲取體重數據的過程是一個請求接口的異步過程
const getFamilyWeight = person => {
  return sleep(2000).then(() => family[person])
}

const loop_forEach = async () => {
  console.log('start')
  let promise = []
  // 在回調函數中,異步是很差控制的
  familyToGet.forEach(person => {
    const num = getFamilyWeight(person)
    promise.push(num)
  })
  const result = await Promise.all(promise)
  console.log('result', result)
  const weight = result.reduce((sum, personWeight) => sum + personWeight)
  console.log('weight', weight)
  console.log('end')
}
loop_forEach()
// 運行結果
// start
// result [150, 100, 200]
// weight 450
// end
複製代碼

在 forEach 的回調函數內,直接把 getFamilyWeight 方法返回的 promise 對象 push 到 promise 這個數組變量內,經過 Promise.all 來作一層等待的異步處理。this

map 遍歷

在 map 中,直接返回一個 promise 數組

const family = {
  "dad": 150,
  "mom": 100,
  "son": 200
}
const familyToGet = ["dad", "mom", "son"]


// 寫一個sleep方法
const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms))
}
// 假設獲取體重數據的過程是一個請求接口的異步過程
const getFamilyWeight = person => {
  return sleep(2000).then(() => family[person])
}

const loop_map = async () => {
  console.log('start')
  // map中返回的是一個promise數組,須要經過Promise.all處理
  const promise = familyToGet.map(async person => {
    const num = await getFamilyWeight(person)
    return num;
  })
  console.log('promise', promise)
  // 等待promise裏的內容所有返回,纔會繼續往下執行
  const result = await Promise.all(promise)
  console.log('result', result)
  const weight = result.reduce((sum, personWeight) => sum + personWeight)
  console.log('weight', weight)
  console.log('end')
}

loop_map()

// 運行結果
// start
// promise [Promise, Promise, Promise]
// result [150, 100, 200]
// weight 450
// end
複製代碼

你們應該能感受到其實這個和上面改版後的 forEach 差很少,沒錯,正如你所料,只不過 map 方法能返回新的數組罷了,forEach 則不能返回數組。

在這裏把 map 的簡單實現寫一下,方便理解

Array.prototype.map = function(cb) {
  // this 爲當前調用該函數的變量
  var o = [];
  for(let i=0; i < this.length; i++) {
      var temp = cb(this[i], i);
      o.push(temp);
  }
  return o;
}
複製代碼

如上述代碼,在外面經過 o 這個變量收集 cb() 回調函數的返回值,再到外面統一處理。真是妙啊,你們能夠細細品味一番,能吾出不少新的對象。

總結

一、for循環內使用async和await仍是能夠的,穩如老狗🐶

二、不要在forEach方法內使用async、await,儘可能避免這種寫法,坑啊。。。

相關文章
相關標籤/搜索