1.先分清几个promise方法的区别:
Promise.any(): 有一个子实例(promise对象)成功才算成功,全部子实例失败才算失败。
Promise.race(): race是竞赛的意思,即看最先的promise子实例是成功还是失败,则它就与最先的子实例状态相同(无论成功与失败)。
Promise.all(): 全部子实例(promise对象)成功才算成功,一个子实例失败就算失败。
Promise.allSettled(): 所有子实例状态都返回结果,不管子实例是成功还是失败,包装实例才执行,不过状态总是变成成功 。
2.使用构造函数的方式创建线程池:
创建一个构造函数,可以传参数limit,callback,其中limit就是我们要限制的并发量,callback是所有promise执行完了之后的回调函数;
自定义并发池pool属性和存储请求地址的urls属性并初始化为[],自定义的limit和callback等于传递进来的对应参数;
class PromisePool{
constructor(limit, callback){
this.limit = limit; // limit为每次的并发量
this.pool = []; //初始化并发池
this.callback = callback; //所有任务执行完的回调
this.urls = []; //urls地址为所有的请求地址数组
}
}
定义一个运行promise任务的方法,其中用到的this.mockFetch()是用于模拟http请求的一个方法,this.addTask()是往线程池里面添加任务。在task的finially时,使用Promise.any()或者Promise.race()获取线程池任意一个要完成的任务,如果在意必须要获取到一个成功的结果,请使用Promise.any(),如果不在意成功与失败,请使用Promise.race()
// 运行任务
runTask(url){
console.time('计时');
let task = this.mockFetch(url);
// this.pool里面的是promise任务数组
if(this.pool.length < this.limit) {
this.pool.push(task);
}
task.then((res) => {
console.log('成功:',res);
console.timeEnd('计时');
}).catch((err) => {
console.log('错误:',err);
console.timeEnd('计时');
}).finally(()=>{
// 利用promise.any或者promise.race方法获取某个任务完成的信号
let data = Promise.any(this.pool);
// 每个请求结束后,将该promise请求从并发池移除
// 成功和失败都要执行移除和往并发池添加新任务的动作
this.pool = this.pool.filter((ele) => ele != task);
this.addTask(data);
if (this.pool.length === 0) {
this.callback('执行完了');
}
})
}
// 再次往线程池添加任务
addTask(task){
task.finally(res => {
if (this.urls.length > 0) {
let url = this.urls.shift();
// 一有一个任务完成,就再塞下一个
this.runTask(url);
}
});
}
// 模拟http请求
mockFetch(param){
return new Promise((resolve, reject) => {
setTimeout(() => {
if(param.info%2 === 1){
resolve(param);
} else{
reject(param);
}
}, 3000);
})
}
定义一个初始化方法start,首次运行的时候把线程池全部填满,线程池数组的长度由传入的limit和请求地址长度共同决定:
start(urls){
this.urls = urls;
let len = Math.min(this.urls.length, this.limit);
let initUrl = this.urls.splice(0, len); //切割之后urls还剩len之后的部分
if (initUrl.length > 0) {
// 首次把线程池塞满
for (let i = 0; i < initUrl.length; i++) {
this.runTask(initUrl[i]);
}
}
}
完整的代码示例如下:
class PromisePool{
constructor(limit, callback){
this.limit = limit; // limit为每次的并发量
this.pool = []; //初始化并发池
this.callback = callback; //所有任务执行完的回调
this.urls = []; //urls地址为所有的请求地址数组
}
start(urls){
// 首次初始化并发池
this.urls = urls;
let len = Math.min(this.urls.length, this.limit);
let initUrl = this.urls.splice(0, len); //切割之后urls还剩len之后的部分
if (initUrl.length > 0) {
// 首次把线程池塞满
for (let i = 0; i < initUrl.length; i++) {
this.runTask(initUrl[i]);
}
}
}
// 运行任务
runTask(url){
console.time('计时');
let task = this.mockFetch(url);
// this.pool里面的是promise任务数组
if(this.pool.length < this.limit) {
this.pool.push(task);
}
task.then((res) => {
console.log('成功:',res);
console.timeEnd('计时');
}).catch((err) => {
console.log('错误:',err);
console.timeEnd('计时');
}).finally(()=>{
// 每个请求结束后,将该promise请求从并发池移除
this.pool = this.pool.filter((ele) => ele !== task);
// 利用promise.race或者promise.any方法获取某个执行后的任务
let data = Promise.race(this.pool);
// 成功和失败之后都要执行移除和往并发池添加新promise的动作
this.addTask(data);
if (this.pool.length === 0 && this.urls.length === 0) {
this.callback('执行完了');
}
})
}
// 再次往线程池添加任务
addTask(task){
task.finally(res => {
if (this.urls.length > 0) {
let url = this.urls.shift();
// 一有一个任务完成,就再塞下一个
this.runTask(url);
}
});
}
// 模拟http请求
mockFetch(param){
return new Promise((resolve, reject) => {
setTimeout(() => {
if(param.info%2 === 1){
resolve(param);
} else{
reject(param);
}
}, 3000);
})
}
}
const pool = new PromisePool(5, (res) => {
console.log(res)
});
const urls = []
for(let i = 0; i < 100; i++){
urls.push({
info: i+1,
data: (i+1)*100
});
}
pool.start(urls);
扫码关注二师兄微信公众号
文章若有错误,恳请大家指出问题所在,本人不胜感激 。不懂的地方可以评论,我都会一一回复。文章对大家有帮助的话,希望大家能动手点赞鼓励。