通过一个 BUG 提醒大家,不要过度依赖 Foreach !
最近写代码时遇到一个bug,问题就是列表渲染不出来,拿不到数据,代码如下
const data = []
const urls = ['name1', 'name2']
urls.forEach(async (url) => {
const v = await request(url)
data.push(v)
})
this.data = data
// 一直是空数组
console.log(this.data)
这个问题还是先了解forEach的实现原理是什么
forEach 实现原理
forEach的实现原理非常简单,如下:
Array.prototype.myForEach = function(callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
}
所以说,在 forEach中使用 async/await 是不可取的,因为它的 callback 是并行执行的,并没有排队效果,那么 data 自然就获取不到,因为 async 是异步的
解决问题
所以需要使用 Promise.all 即可
const urls = ['api1', 'api2']
const data = await Promise.all
urls.map(url => request(url))
)
this.data = data
不要过度依赖 forEach
很多人都基本只用 forEach,但是它是有很多缺点的
- 无法中断
- 无法进行异步排队
所以不要过度依赖它
中断、排队 可以用 for
其实直接用 for 循环也可以实现排队、中断的效果,这都是 forEach做不到的
data.forEach(item => {
// 没有效果
if (xxx) break;
})
for (let item of data) {
// 有效果
if (xxx) break;
}
data.forEach(async item => {
// 没有效果
await item()
})
for (let item of data) {
// 有效果
await item()
}
合理使用 filter、map、reduce
如果你需要过滤一个数组,可以使用 filter
const result = []
data.forEach(item => {
if (item.flag) {
result.push(item)
}
})
// 用 filter
const result =
data.filter(({ flag }) => flag)
如果你需要格式化数组数据,你可以使用 map
const result = []
data.forEach(item => {
result.push({
name: item.name,
age: `${item.age}岁`
})
})
// 使用 map
const result =
data.map(item =({
name: item.name,
age: `${item.age}岁`
}))
如果你需要累计,你可以使用 reduce
const sum = 0
nums.forEach(num => {
sum += num
})
// 使用 reduce
const sum =
nums.reduce((pre, next) => {
const v = pre + next
return v
}, 0)