35.Promise-如何处理异步数组
# 0.前言
本章节核心工作就是:梳理异步编码写法。
# 1.Promise 思考
# 如何取数组数据?
对于大部分的前端面试题处理都是异步执行列表,因此第一步思考的是如何处理这个数组?
一般而言,异步执行列表中的元素有两种类型:普通数据及Promise
对象。
// 1. 纯数据数组
let promiseLit = [1, 2, 3, 4, 5];
// 2. Promise任务 和 数据混合数组
let promiseLit = [Promise.resolve(1), 2, 3, 4, 5];
1
2
3
4
2
3
4
特别注意,在上述数组中的 Promise
的异步事件,也是同步执行代码,如果立即打印:
let promiseList = [Promise.resolve(1),Promise.reject(2),Promise任务];
// 直接打印结果
[ Promise {<fulfilled>: 1}, Promise {<rejected>: 1},Promise{<pending>}];
1
2
3
2
3
因此,需要使用 Promise.resolve
取出数据
promiseList.map((p) =>
Promise.resolve(p).then((result) => console.log(result)),
);
1
2
3
2
3
# 异步列表的串行执行
串行的核心:就是在上一层的结果的基础上继续进行某种操作,如果不使用 promise
,而使用 callback
方式,从结构上来讲就是嵌套类型。
# 写法一:for
循环 + 函数包装
基本结构:
async function xxx(arr) {
let res;
for (let i = 0; i < arr.length; i +) {
if (i === 0) {
// 处理初始值
} else {
res = await + /* promise异步任务 */
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
因为 async
和 await
这种方式具有传染性,因此需要在外部包装一个函数。
# 写法二:构建 promise
的 .then
串
伪代码如下:
arr.slice(1).reduce((pre, cur) => {
/* 获取 cur 的异步结果 */
Promise.resolve(cur).then(curValue => ......);
/* 获取 pre 的异步结果 */
pre.then(result =>.....);
/* 当两个异步任务都完成后,将各自的结果传入一个异步任务并返回*/
return asyncAdd(result, curValue);
}, Promise.resolve(arr[0]);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 异步列表的并行执行
按照题型分为:合并一个结果及数组结果。
# 方案一:使用分组的方式实现并行
将数组分组:使用
chunk
function chunk(list, size) { const res = []; for (let i = 0; i < list.length; i++) { const index = Math.floor(i / size); res[index] = res[index] || []; res[index].push(list[i]); } return res; }
1
2
3
4
5
6
7
8
9将数组分组:
chunk(arr,2)
结果为:
[1,2,3,4,5,6,7]=>[[1,2],[3,4],[5,6],[7]]
使用
arr.map
构造Promise
队列,即[Promise<pending,1+2>,Promise<pending,3+4>,Promise<pending,5+6>,Promise<pending,7>
1偷懒了:直接使用
Promise.all
进行串行转化为 数组结构。Promise.all([xxx,xxx,xxx]).then(result=>....);
1最后递归调用,并设置截止条件:
// 最后结果为: [[15]] if(arr.length) === 1 return arr[0];
1
2
# 2. 综合案例-异步 sum
累加
请实现一个 sum
函数,接收一个数组 arr
进行累加,并且只能使用 add
异步方法
add 函数已实现,模拟异步请求后端返回一个相加后的值
sum([1, 2, 3, 4, 5]).then((o) => console.log(o));
// 异步相加函数
function add(a, b) {
return Promise.resolve(a + b);
}
1
2
3
4
5
6
2
3
4
5
6
串行实现
使用
reduce
构建执行串function sum(arr) { return arr.reduce((pre, cur) => { return Promise.resolve(pre).then((preRes) => add(preRes, cur)); }); }
1
2
3
4
5改进版,使用
.slice(1).reduce
技巧function sum(arr) { return arr.slice(1).reduce((pre, cur) => { return pre.then((preRes) => add(preRes, cur)); }, Promise.resolve(arr[0])); }
1
2
3
4
5使用
await
+async
实现async function sum(arr) { let res = arr[0]; // 使用 res 维护一个持续累加的变量 for (let i = 1; i < arr.length; i++) { res = await add(res, arr[i]); } }
1
2
3
4
5
6存在的风险是,
await
可能会被错误拦截,需要使用try...catch
去捕获错误。
并行实现
function sum(arr: any[]) { // 递归截止条件,结果为: [[ 15 ]] if (arr.length === 1) return arr[0]; // 将 arr 封装为 promiseTask const promiseTasks = chunk(arr, 2).map(([x, y]) => { return y === undefined ? x : add(x, y); }); // 结果为:[add1,add2,add3,x] return Promise.all(promiseTasks).then((list) => sum(list)); } /* 分组函数*/ function chunk(list, size = 2) { const res = []; for (let i = 0; i < list.length; i++) { const index = Math.floor(i / size); res[index] = res[index] || []; res[index].push(list[i]); } return res; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
编辑 (opens new window)
上次更新: 2023/10/02, 17:10:00