js promise 遍历

原文链接: js promise 遍历

上一篇: 汇编 双重循环累加求和

下一篇: pyecharts 简单使用

前言

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 接收两个参数:

  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 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 ,终止遍历。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值