Array对象方法中使用Async

这篇文章主要介绍了Array.map、Array.filter、Array.reduce中如何正确使用Async。
在写网关接口处理复杂数据场景(串行、并行、异步过滤)时很有帮助。

Array 对象方法

方法 描述
concat() 连接两个或更多的数组,并返回结果。
copyWithin() 从数组的指定位置拷贝元素到数组的另一个指定位置中。
entries() 返回数组的可迭代对象。
every() 检测数值元素的每个元素是否都符合条件。
fill() 使用一个固定值来填充数组。
filter() 检测数值元素,并返回符合条件所有元素的数组。
find() 返回符合传入测试(函数)条件的数组元素。
findIndex() 返回符合传入测试(函数)条件的数组元素索引。
forEach() 数组每个元素都执行一次回调函数。
from() 通过给定的对象中创建一个数组。
includes() 判断一个数组是否包含一个指定的值。
indexOf() 搜索数组中的元素,并返回它所在的位置。
isArray() 判断对象是否为数组。
join() 把数组的所有元素放入一个字符串。
keys() 返回数组的可迭代对象,包含原始数组的键(key)。
lastIndexOf() 搜索数组中的元素,并返回它最后出现的位置。
map() 通过指定函数处理数组的每个元素,并返回处理后的数组。
pop() 删除数组的最后一个元素并返回删除的元素。
push() 向数组的末尾添加一个或更多元素,并返回新的长度。
reduce() 将数组元素计算为一个值(从左到右)。
reduceRight() 将数组元素计算为一个值(从右到左)。
reverse() 反转数组的元素顺序。
shift() 删除并返回数组的第一个元素。
slice() 选取数组的一部分,并返回一个新数组。
some() 检测数组元素中是否有元素符合指定条件。
sort() 对数组的元素进行排序。
splice() 从数组中添加或删除元素。
toString() 把数组转换为字符串,并返回结果。
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
valueOf() 返回数组对象的原始值。

串行

数组和模拟请求,后面都会用到

1
2
3
4
5
6
7
8
const arr = [1, 2, 3, 4, 5];
const delay = async (time) => {
await new Promise(resolve => {
setTimeout(() => {
resolve()
}, time)
})
}

简单处理串行请求

1
2
3
4
5
6
7
8
9
const arraySequentially = async (arr) => {
const result = await arr.reduce(async (total, item, index) => {
await total
await delay(100)
console.log(item)
return arr.length !== (index + 1) ? Promise.resolve() : 'end'
}, Promise.resolve())
return result
}

并行

map 并行

1
2
3
4
5
6
7
8
const asyncMap = async () => {
const result = await Promise.all(arr.map(async item => {
await delay(100)
console.log(item)
return item
}))
return result
}

filter 结合 map 使用

1
2
3
4
5
6
7
8
9
10
11
const asyncFilter = async (arr, predicate) => {
const results = await Promise.all(arr.map(predicate));
return arr.filter((_v, index) => results[index]);
}
const asyncRes = await asyncFilter(arr, async (i) => {
await delay(100)
return i % 2 === 0;
});

console.log(asyncRes);
// 2,4

改为单行

1
const asyncFilter = async (arr, predicate) => Promise.all(arr.map(predicate)).then((results) => arr.filter((_v, index) => results[index]));

filter 结合 reduce 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
// concurrently 并行
const asyncFilter = async (arr, predicate) =>
arr.reduce(async (memo, e) =>
await predicate(e) ? [...await memo, e] : memo
, []);

const asyncRes = await asyncFilter(arr, async (i) => {
await delay(100)
return i % 2 === 0;
});

console.log(asyncRes);
// 2,4

要在调用下一个谓词函数之前等待其结束,请更改await 的顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
// sequentially 串行
const asyncFilter = async (arr, predicate) =>
arr.reduce(async (memo, e) =>
[...await memo, ...await predicate(e) ? [e] : []]
, []);

const asyncRes = await asyncFilter(arr, async (i) => {
await delay(100)
return i % 2 === 0;
});

console.log(asyncRes);
// 2,4

拓展

1.线程是系统调度的最小单位,所以下面的“下一个任务”的说法是指下一个线程,而不是指比线程更小或更大的事物;

2.进程是系统分配资源的基本单位。比如某进程有T1,T2两个线程,T2申请某块内存,操作系统分配时并不是分给T2,而是分给整个进程,这样T1也可以去使用这块内存;

3.为了利用CPU的多核资源,引入多线程概念。多核CPU本质上还是只有一个CPU,每个时刻只能处理一个进程,如果该进程当中有多个线程,那么就可以发挥多核CPU的作用,同时处理多个线程。由于时间片轮转,处理该进程的时间片用完之后就会去处理下一个进程,以达到并发的效果;

4.“串行,并行,并发,同步,异步,顺序”的概念:
串行:多任务中某时刻只能有一个任务被运行(并没有指定任务间的先后顺序,跟口语中的串行不一样);
并行:相对于串行,某时刻有多个任务同时被执行,如多核分别独立执行线程时各个任务刚好在某个时间段有时间上的重叠;
并发:引入时间片和抢占之后才有了并发的说法,某个时间片只有一个任务在执行,执行完时间片后进行资源抢占,到下一个任务去执行,即“微观串行,宏观并发”,所以这种情况下只有一个空闲的某核,多核空闲就又可以实现并行运行了;
同步:函数会阻塞直到任务完成返回才能进行其它操作;
异步:在任务执行完成之前先将函数值返回;
顺序: 就是平常理解的时间先后;

参考链接

https://www.runoob.com/jsref/jsref-obj-array.html
https://www.jb51.net/article/198959.htm
https://www.zhihu.com/question/61755696


Array对象方法中使用Async
http://example.com/20210617-Array对象方法中使用async/
作者
csorz
发布于
2021年6月17日
许可协议