這裏指的遍歷要領包含:
map
、
reduce
、
reduceRight
、
forEach
、
filter
、
some
、
every
由於近來要舉行了一些數據匯總,
node
版本已是8.11.1了,所以直接寫了個
async/await
的劇本。然則在對數組舉行一些遍歷操縱時,發明有些遍歷要領對
Promise
的反應並非我們想要的結果。
固然,有些嚴厲來講並不能算是遍歷,比如說some
,every
這些的。
但確切,這些都邑依據我們數組的元夙來舉行屢次的挪用傳入的回調。
這些要領都是比較罕見的,然則當你的回調函數是一個Promise
時,一切都變了。
媒介
async/await
為Promise
的語法糖
文中會直接運用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
吸收兩個參數:
- 對每項元素實行的回調,回調結果的返回值將作為該數組中響應下標的元素
- 一個可選的回調函數
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
的函數署名想必人人也很熟悉了,吸收兩個參數:
對每一項元素實行的回調函數,返回值將被累加到下次函數挪用中,回調函數的署名:
-
accumulator
累加的值 -
currentValue
當前正在處置懲罰的元素 -
currentIndex
當前正在處置懲罰的元素下標 -
array
挪用reduce
的數組
-
- 可選的初始化的值,將作為
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
,這個應該是用得最多的遍歷要領了,對應的函數署名:
callback
,對每個元素舉行挪用的函數-
currentValue
,當前元素 -
index
,當前元素下標 -
array
,挪用forEach
的數組援用
-
-
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 0
、await 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
,停止遍歷。
跋文
關於數組的這幾個遍歷要領。
由於map
和reduce
的特徵,所以是在運用async
時修改最小的函數。reduce
的結果很像一個洋蔥模子
但關於其他的遍歷函數來講,現在來看就須要本身來完成了。
四個*Sync
函數的完成:https://github.com/Jiasm/notebook/tree/master/array-sync