Map, reduce, reduceRight, forEach, filter, some, every: map, reduce, reduceRight, forEach, filter, some, every: map, reduce, reduceRight, forEach, filter, some, every: map, reduce, reduceRight, forEach, filter, some, every However, when we did some traversal operations on the array, we found that some of the traversal methods did not give the desired result to the Promise.

Of course, some of them are not technically traversal, like some, every. But it’s true that these are going to be called multiple times depending on the elements of our array and the callbacks that we pass in.

These methods are fairly common, but everything changes when your callback is a Promise.

preface

Async /await is a Promise syntax that directly replaces the Promise with async/await

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

/ / = = = = = =

async function func () {
  return 1  
}
// =
function func () {
  return new Promise(resolve= > resolve(1))}Copy the code

map

Map is arguably the most promise-friendly function. As we all know, map takes two parameters:

  1. A callback is performed for each element, and the return value of the result of the callback will be the element in the array at the corresponding index
  2. An optional callback functionthisPointing parameter
[1.2.3].map(item= > item ** 2) // Square the elements of the array
// > [1, 4, 9]
Copy the code

Above is a normal map execution, but when some of our computations become asynchronous:

[1.2.3].map(async item => item ** 2) // Square the elements of the array
// > [Promise, Promise, Promise]
Copy the code

In this case, the return value is an array of Promise functions.

The map function is the friendliest because we know that Promise has a function called Promise.all that executes an array of promises and returns a Promise object whose result is the result set produced by the array.

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

The first to usePromise.allWrap the array, and then useawaitGet the results.

reduce/reduceRight

The reduce function signature takes two parameters:

  1. For each element of the callback function, the return value will be added to the next function call.
    1. accumulatorThe value of the cumulative
    2. currentValueThe element currently being processed
    3. currentIndexThe index of the element currently being processedThank you,@NeurotoxinVXCompanion’s supplement)
    4. arraycallreduceAn array of
  2. Optional initialization value that will be used asaccumulatorThe initial value of the
[1.2.3].reduce((accumulator, item) = > accumulator + item, 0) // Add the sum
/ / > 6
Copy the code

This code is also fine if we add the sum asynchronously:

[1.2.3].reduce(async (accumulator, item) => accumulator + item, 0) // Add the sum
// > Promise {<resolved>: "[object Promise]3"}
Copy the code

This result will return something very strange. Let’s look back at the reduce function signature above

For each element of the callback function, the return value is added to the next function call

Async (accumulator, item) => ACCUMULator += item

(accumulator, item) => new Promise(resolve= >
  resolve(accumulator += item)
)
Copy the code

In other words, the return value of our reduce callback function is actually a Promise object and then we apply += to the Promise object and it makes sense to get that weird return value.

Of course, reduce is easy to adjust:

await [1.2.3].reduce(async (accumulator, item) => await accumulator + item, 0)
/ / > 6
Copy the code

We call “await” on the accumulator, and then we sum it with the current item. At the end our reduce return must also be a Promise, so we add “await” on the outermost part. This means that every time we reduce we return a new Promise, The result of the last Promise is retrieved inside the object. What we actually get when we call Reduce is a Promise object that looks like this:

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))
})
Copy the code

reduceRight

There is nothing more to say about this. The order of execution is reversed from reduce

forEach

ForEach, which is probably the most used traversal method, corresponds to the function signature:

  1. callbackThe function that is called on each element
    1. currentValue, the current element
    2. index, the index of the current element
    3. array, the callforEachArray reference
  2. thisArg, an optional callback functionthisPoint to the

We have the following operations:

// Get the square value of the array element
[1.2.3].forEach(item= > {
  console.log(item ** 2)})/ / > 1
/ / > 4
/ / > 9
Copy the code

We can do this with the normal version, but if we encounter a Promise

// Get the square value of the array element
[1.2.3].forEach(async item => {
  console.log(item ** 2)})// > nothing
Copy the code

ForEach doesn’t care about the return value of the callback function, so forEach just executes three functions that return promises. So if we want to get the desired effect, we can only enhance the object properties ourselves:

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
Copy the code

awaitIgnore thePromiseValue,await 0,await undefinedJust like normal code

filter

The filter function, which filters an array, also has the same traversal function: the function signature is the same as forEach, but elements that are returned true by the callback function will be placed in the filter return value.

We want to do an odd number filter, so we write:

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

Then we changed to the Promise version:

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

This invalidates our filter because the return value of the filter is not an exact match, and any return value that can be converted to true is considered to have passed the filter. The Promise object must be true, so filtering is invalid. So we’re going to do the same thing as forEach, we’re going to do our own object enhancement but we’re going to do it in a more subtle way:

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)
Copy the code

We can call the map method internally directly because we know that map will return all the return values as a new array. What this means is that our map can get the results of our filter for all items, true or false. Next, each item in the original array will return the result of the corresponding subscript.

some

Some is a function that checks whether an array meets certain conditions. It is also a function signature that can be used as a traversal. The difference is that forEach returns true if any callback value matches true, and false if all callback values match false

We want to determine if any element in the array is equal to 2:

[1.2.3].some(item= > item === 2)
// > true
Copy the code

Then we change it to a Promise

[1.2.3].some(async item => item === 2)
// > true
Copy the code

This function will still return true, but it’s not what we want because the Promise object returned by async is supposed to be true.

So, we need to do the following:

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
Copy the code

Since some terminates the traversal after matching the first true, it would be a waste of performance to use forEach here. We also take advantage of the fact that await ignores ordinary expressions and uses for-of internally to implement our requirements

every

And our final every function has the same signature as forEach, but the callback is handled differently: Or another way to think about it is every is an inverse some some terminates on the first true and every terminates on the first false, and returns true if all the elements are true

We want to determine whether all the elements in the array are greater than 3

[1.2.3].every(item= > item > 3)
// > false
Copy the code

Obviously, none of these matches were found, and the callback function terminates after the first execution and does not continue. We changed to the Promise version:

[1.2.3].every(async= > item > 3)
// > true
Copy the code

This must be true because we are judging a Promise object so let’s take the someSync implementation above and modify it a little:

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
Copy the code

If any false is matched, false is returned and the traversal is terminated.

Afterword.

These are the iterating methods for the array. Because of the features of Map and Reduce, they are the least modified functions when using Async. The result of Reduce is very much like an onion model, but for the other traversal functions, you need to implement it yourself for now.

Four *Sync functions: github.com/Jiasm/noteb…

The resources

Array – JavaScript | MDN