數組的遍歷你都邑用了,那Promise版本的呢

這裏指的遍歷要領包含:
map
reduce
reduceRight
forEach
filter
some
every

由於近來要舉行了一些數據匯總,
node版本已是8.11.1了,所以直接寫了個
async/await的劇本。

然則在對數組舉行一些遍歷操縱時,發明有些遍歷要領對
Promise的反應並非我們想要的結果。

固然,有些嚴厲來講並不能算是遍歷,比如說someevery這些的。
但確切,這些都邑依據我們數組的元夙來舉行屢次的挪用傳入的回調。

這些要領都是比較罕見的,然則當你的回調函數是一個Promise時,一切都變了。

媒介

async/awaitPromise的語法糖
文中會直接運用async/await替代Promise

let result = await func()
// => 等價於
func().then(result => {
  // code here
})

// ======

async function func () {
  return 1  
}
// => 等價與
function func () {
  return new Promise(resolve => resolve(1))
}

map

map可以說是對Promise最友愛的一個函數了。
我們都曉得,map吸收兩個參數:

  1. 對每項元素實行的回調,回調結果的返回值將作為該數組中響應下標的元素
  2. 一個可選的回調函數this指向的參數
[1, 2, 3].map(item => item ** 2) // 對數組元素舉行求平方
// > [1, 4, 9]

上邊是一個一般的map實行,然則當我們的一些盤算操縱變成異步的:

[1, 2, 3].map(async item => item ** 2) // 對數組元素舉行求平方
// > [Promise, Promise, Promise]

這時候,我們獵取到的返回值實在就是一個由Promise函數構成的數組了。

所以為何上邊說map函數為最友愛的,由於我們曉得,Promise有一個函數為Promise.all
會將一個由Promise構成的數組順次實行,並返回一個Promise對象,該對象的結果為數組發生的結果集。

await Promise.all([1, 2, 3].map(async item => item ** 2))
// > [1, 4, 9]

起首運用Promise.all對數組舉行包裝,然後用await獵取結果。

reduce/reduceRight

reduce的函數署名想必人人也很熟悉了,吸收兩個參數:

  1. 對每一項元素實行的回調函數,返回值將被累加到下次函數挪用中,回調函數的署名:

    1. accumulator累加的值
    2. currentValue當前正在處置懲罰的元素
    3. currentIndex當前正在處置懲罰的元素下標
    4. array挪用reduce的數組
  2. 可選的初始化的值,將作為accumulator的初始值
[1, 2, 3].reduce((accumulator, item) => accumulator + item, 0) // 舉行加和
// > 6

這個代碼也是沒缺點的,一樣假如我們加和的操縱也是個異步的:

[1, 2, 3].reduce(async (accumulator, item) => accumulator + item, 0) // 舉行加和
// > Promise {<resolved>: "[object Promise]3"}

這個結果返回的就會很詭異了,我們在回看上邊的reduce的函數署名

對每一項元素實行的回調函數,返回值將被累加到下次函數挪用中

然後我們再來看代碼,async (accumulator, item) => accumulator += item
這個在最最先也提到了,是Pormise的語法糖,為了看得更清楚,我們可以如許寫:

(accumulator, item) => new Promise(resolve =>
  resolve(accumulator += item)
)

也就是說,我們reduce的回調函數返回值實在就是一個Promise對象
然後我們對Promise對象舉行+=操縱,獲得那樣奇異的返回值也就很通情達理了。

固然,reduce的調解也是很輕鬆的:

await [1, 2, 3].reduce(async (accumulator, item) => await accumulator + item, 0)
// > 6

我們對accumulator挪用await,然後再與當前item舉行加和,在末了我們的reduce返回值也一定是一個Promise,所以我們在最外邊也增加await的字樣
也就是說我們每次reduce都邑返回一個新的Promise對象,在對象內部都邑獵取上次Promise的結果。
我們挪用reduce實際上獲得的是相似如許的一個Promise對象:

new Promise(resolve => {
  let item = 3
  new Promise(resolve => {
      let item = 2
      new Promise(resolve => {
        let item = 1
        Promise.resolve(0).then(result => resolve(item + result))
      }).then(result => resolve(item + result))
  }).then(result => resolve(item + result))
})

reduceRight

這個就沒什麼好說的了。。跟reduce只是實行遞次相反罷了

forEach

forEach,這個應該是用得最多的遍歷要領了,對應的函數署名:

  1. callback,對每個元素舉行挪用的函數

    1. currentValue,當前元素
    2. index,當前元素下標
    3. array,挪用forEach的數組援用
  2. thisArg,一個可選的回調函數this指向

我們有以下的操縱:

// 獵取數組元素求平方后的值
[1, 2, 3].forEach(item => {
  console.log(item ** 2)
})
// > 1
// > 4
// > 9

一般版本我們是可以直接這麼輸出的,然則假如遇到了Promise

// 獵取數組元素求平方后的值
[1, 2, 3].forEach(async item => {
  console.log(item ** 2)
})
// > nothing

forEach並不體貼回調函數的返回值,所以forEach只是實行了三個會返回Promise的函數
所以假如我們想要獲得想要的結果,只可以本身舉行加強對象屬性了:

Array.prototype.forEachSync = async function (callback, thisArg) {
  for (let [index, item] of Object.entries(this)) {
    await callback(item, index, this)
  }
}

await [1, 2, 3].forEachSync(async item => {
  console.log(item ** 2)
})

// > 1
// > 4
// > 9

await會疏忽非Promise值,await 0await undefined與一般代碼無異

filter

filter作為一個挑選數組用的函數,一樣具有遍歷的功用:
函數署名同forEach,然則callback返回值為true的元素將被放到filter函數返回值中去。

我們要舉行一個奇數的挑選,所以我們這麼寫:

[1, 2, 3].filter(item => item % 2 !== 0)
// > [1, 3]

然後我們改成Promise版本:

[1, 2, 3].filter(async item => item % 2 !== 0)
// > [1, 2, 3]

這會致使我們的挑選功用失效,由於filter的返回值婚配不是完整相稱的婚配,只如果返回值能轉換為true,就會被認定為經由過程挑選。
Promise對象必定是true的,所以挑選失效。
所以我們的處置懲罰體式格局與上邊的forEach相似,一樣須要本身舉行對象加強
但我們這裏直接挑選一個取巧的體式格局:

Array.prototype.filterSync = async function (callback, thisArg) {
  let filterResult = await Promise.all(this.map(callback))
  // > [true, false, true]

  return this.filter((_, index) => filterResult[index])
}

await [1, 2, 3].filterSync(item => item % 2 !== 0)

我們可以直接在內部挪用map要領,由於我們曉得map會將一切的返回值返回為一個新的數組。
這也就意味着,我們map可以拿到我們對一切item舉行挑選的結果,true或許false
接下來對原數組每一項舉行返回對應下標的結果即可。

some

some作為一個用來檢測數組是不是滿足一些前提的函數存在,一樣是可以用作遍歷的
函數署名同forEach,有區分的是當任一callback返回值婚配為true則會直接返回true,假如一切的callback婚配均為false,則返回false

我們要推斷數組中是不是有元素即是2

[1, 2, 3].some(item => item === 2)
// > true

然後我們將它改成Promise

[1, 2, 3].some(async item => item === 2)
// > true

這個函數依舊會返回true,然則卻不是我們想要的,由於這個是async返回的Promise對象被認定為true

所以,我們要舉行以下處置懲罰:

Array.prototype.someSync = async function (callback, thisArg) {
  for (let [index, item] of Object.entries(this)) {
    if (await callback(item, index, this)) return true
  }

  return false
}
await [1, 2, 3].someSync(async item => item === 2)
// > true

由於some在婚配到第一個true以後就會停止遍歷,所以我們在這裏邊運用forEach的話是在性能上的一種糟蹋。
一樣是利用了await會疏忽一般表達式的上風,在內部運用for-of來完成我們的需求

every

以及我們末了的一個every
函數署名一樣與forEach一樣,
然則callback的處置懲罰照樣有一些區分的:
實在換一種角度斟酌,every就是一個反向的some
some會在獵取到第一個true時停止
every會在獵取到第一個false時停止,假如一切元素均為true,則返回true

我們要剖斷數組中元素是不是悉數大於3

[1, 2, 3].every(item => item > 3)
// > false

很顯然,一個都沒有婚配到的,而且回調函數在實行到第一次時就已停止了,不會繼承實行下去。
我們改成Promise版本:

[1, 2, 3].every(async => item > 3)
// > true

這個必定是true,由於我們推斷的是Promise對象
所以我們拿上邊的someSync完成輕微修正一下:

Array.prototype.everySync = async function (callback, thisArg) {
  for (let [index, item] of Object.entries(this)) {
    if (!await callback(item, index, this)) return false
  }

  return true
}
await [1, 2, 3].everySync(async item => item === 2)
// > false

當婚配到恣意一個false時,直接返回false,停止遍歷。

跋文

關於數組的這幾個遍歷要領。
由於mapreduce的特徵,所以是在運用async時修改最小的函數。
reduce的結果很像一個洋蔥模子
但關於其他的遍歷函數來講,現在來看就須要本身來完成了。

四個*Sync函數的完成:https://github.com/Jiasm/notebook/tree/master/array-sync

參考資料

Array – JavaScript | MDN

    原文作者:賈順名
    原文地址: https://segmentfault.com/a/1190000014598785
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞