promise高级用法: 同步执行,并发控制,并发控制并获取全部执行结果
原文链接:https://www.cnblogs.com/yalong/p/16038528.html
本文展示promise的三种用法
- promise实现异步代码的同步执行
- promise控制大量请求的并发执行
- promise控制大量请求的并发执行,并获取全部执行结果
一.Promise实现异步代码的同步执行
场景描述:
有fn1 、fn2 、 fn3三个异步任务,要求必须先执行fn1,再执行fn2,最后执行fn3
且下一次任务必须要拿到上一次任务执行的结果,才能做操作
三个异步函数如下:
// 异步函数fn1
const fn1 = function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111')
}, 500)
})
}
// 异步函数fn2
const fn2 = (data) => {
return new Promise((resolve, reject) => {
resolve(data + '222')
})
}
// 异步函数fn3
const fn3 = (data) => {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve(data + '333')
}, 1000)
})
}
方法一:使用then的链式调用
//链式调用
fn1().then((data) => {
return fn2(data)
}).then((data) => {
return fn3(data)
})
.then((data) => {
console.log(data) // 111222333
})
方法二:使用async await
async function queue(arr) {
let res = null
for (let promise of arr) {
res = await promise(res)
}
return await res // 这里的await可以去掉,因为已经是最后一步了
}
// 因为async返回返回的也是promise,所以可以使用then
queue([fn1, fn2, fn3])
.then(data => {
console.log(data) // 111222333
})
二. Promise并发控制
场景描述一
假设有100个ajax请求,每次发送3个请求,其中一个请求完毕,再加入新的请求,直到全部请求完毕
如果使用promsie.all,浏览器会瞬间发送100个请求,这样可能会造成请求阻塞、页面卡顿、甚至服务器崩溃,显然不合理;
那么就需要设计一个方案
代码实现
// 这个就是每次执行的异步请求方法,参数不一样
const fn = (t) => {
// 用setTimeout模拟异步请求
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('任务完成', t, new Date());
resolve({ t, date: new Date() });
}, t * 1000);
})
};
let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]
/**
* arrs 请求数据源数组
* limit 是每次并行发起多少个请求
* handleFn 就是异步处理函数
*/
function limitQueueFn(arrs, limit, handleFn) {
// 完成任务数
let index = 0;
// 第一次的时候 一次性执行 limit 个任务
for (let i = 0; i < limit; i++) {
run();
}
// 执行一个任务
function run() {
// 构造待执行任务 当该任务完成后 如果还有待完成的任务 继续执行任务
new Promise((resolve, reject) => {
const value = arrs[index];
index++; // 这个是同步操作
// resolve 返回 promise
resolve(handleFn(value))
}).then(() => {
if (index < arrs.length) {
run()
}
})
}
};
limitQueueFn(arr, 3, fn)
执行结果打印如下:
任务完成 1 2022-03-22T03:19:17.907Z
任务完成 1 2022-03-22T03:19:17.911Z
任务完成 1 2022-03-22T03:19:17.911Z
任务完成 2 2022-03-22T03:19:19.913Z
任务完成 2 2022-03-22T03:19:19.914Z
任务完成 2 2022-03-22T03:19:19.915Z
任务完成 3 2022-03-22T03:19:22.919Z
任务完成 3 2022-03-22T03:19:22.919Z
任务完成 3 2022-03-22T03:19:22.919Z
代码分析
- 首先并行发起limit个请求
- 然后就是利用promise.then, 当请求完成之后,就去发起下一个请求
- 使用变量 index 来统计执行了多少个请求,没有执行就就一直执行
场景描述二
上面的代码是可以实现并发请求控制了, 但是如果想获取到全部请求执行完的结果,并且结果也要是有序的,跟 arrs 的顺序一样,这个怎么实现?
代码实现
const fn = (t) => {
// 用setTimeout模拟异步请求
return new Promise((resolve, react) => {
setTimeout(() => {
console.log('任务完成', t, new Date());
resolve({ t, date: new Date() });
}, t * 1000);
})
};
let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]
/**
* arrs 请求数据源数组
* limit 是每次并行发起多少个请求
* handleFn 就是异步处理函数
*/
function limitQueueFn(arrs, limit, handleFn) {
// 完成任务数
let runningIndex = 0; // 这是正在执行的下标
let finishedIndex = 0 // 这是已经执行完的下表
let result = new Array(arrs.length).fill(0) // 建立一个空数组, 存储结果
return new Promise((resolveFn, rejectFn) => {
// 第一次的时候 一次性执行 limit 个任务
for (let i = 0; i < limit; i++) {
run();
}
// 执行一个任务
function run() {
// 构造待执行任务 当该任务完成后 如果还有待完成的任务 继续执行任务
new Promise((resolve, reject) => {
const value = arrs[runningIndex];
runningIndex++; // 这个是同步操作
resolve(handleFn(value))
}).then((res) => {
result[finishedIndex] = res
finishedIndex++
if (runningIndex < arrs.length) {
run()
} else { // 全部执行完毕
resolveFn(result)
}
})
}
})
};
limitQueueFn(arr, 3, fn).then(res => {
console.log('结果如下:')
console.log(res)
})
执行结果打印如下:
任务完成 1 2022-03-22T03:18:10.420Z
任务完成 1 2022-03-22T03:18:10.426Z
任务完成 1 2022-03-22T03:18:10.426Z
任务完成 2 2022-03-22T03:18:12.428Z
任务完成 2 2022-03-22T03:18:12.430Z
任务完成 2 2022-03-22T03:18:12.430Z
任务完成 3 2022-03-22T03:18:15.435Z
任务完成 3 2022-03-22T03:18:15.436Z
任务完成 3 2022-03-22T03:18:15.436Z
结果如下:
[ { t: 1, date: 2022-03-22T03:18:10.426Z },
{ t: 1, date: 2022-03-22T03:18:10.426Z },
{ t: 1, date: 2022-03-22T03:18:10.426Z },
{ t: 2, date: 2022-03-22T03:18:12.429Z },
{ t: 2, date: 2022-03-22T03:18:12.430Z },
{ t: 2, date: 2022-03-22T03:18:12.430Z },
{ t: 3, date: 2022-03-22T03:18:15.436Z },
{ t: 3, date: 2022-03-22T03:18:15.436Z },
{ t: 3, date: 2022-03-22T03:18:15.436Z } ]