1.promise原理分析
2.promise并发限制实现
场景:前面的三个请求都先发送请求,前面三个请求中任意一个返回后,后面按照顺序补上发送。
核心原理:并发限制,分别要实现最大请求数量,然后前面1,2,3其中有一个请求成功返回后,后面就要补上4。
这里的关键在于:
1.前面的1,2,3不知道哪个先返回成功;
2.返回成功之后后面跟上的,是按照顺序进行跟上的;
3.“4”执行成功之后也要继续(意味着后面补上来之后,并不知道队列中是否还存在,如果存在的话还要继续从队列中获取执行)
从这里可以看出,我们要想实现2,那么就要在每个promise后面加上then,并且执行quene.shift()().then(),其中then函数中执行队列的shift()和shift之后的函数执行。
但是这个函数执行之后,如果有返回也要实现相同的功能,因此这里的相同就意味着需要递归。也就是说,then里面执行的是一个递归函数,递归的逻辑就是:弹出队列,执行函数,并在函数的then中再执行递归
const url = [url1, url2, url3, url4, url5, url6, url7]
// 要求实现请求并发,最大数量为3
class schere {
constructor(list, max) {
this.promiseList = this.initial(list);
this.max = max;
this.count = 0;
}
// 1.xhr初始化
initial(list) {
let result = list.map((url) => {
return function() {
new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onload = function(res) {
resolve(res);
}
xhr.open('get',url);
xhr.send(null);
})
}
});
return result;
}
// 2.开始
start() {
for (let i = 0; i < this.max; i++) {
// 以下为重复的逻辑,转化为递归函数
// let promise = this.promiseList.shift();
// promise().then(() => {
// count--;
// })
this.recursion();
}
}
// 3.递归逻辑
recursion() {
let a = this.promiseList.shift();
count++;
a().then(() => {
count--;
this.recursion;
})
}
}
new schere(url, 3).start(); // 开始执行
这里要知道,promise并发请求限制,有两个常见的功能,一个是start()启动,另一个是add或者直接将发送的数组URL中。
这里的start是使用for循环执行递归。
关键在于这个递归,将数组中的promise元素pop出来,然后执行,执行结束成功之后的then是“关键”,继续将数组中的元素pop出来,然后执行,然后then中放入递归。总之一句话,就是递归函数中shift元素,元素执行的then中再放入递归,即then函数中放入递归。
3.promise.retry实现
promise.retry的作用只有一个,就是当promise状态为reject的时候,重新执行之前的函数。例如,getNum函数是一个返回promise的函数,我希望如果getNum函数如果执行失败(在promise中执行),重新执行(执行次数有限制)
let getNum = function () {
console.log("函数执行一次");
return new Promise((res, rej) => {
let num = Math.random() * 10;
num < 2 ? res("数字小于2") : rej("数字大于2");
});
};
const retry = function(fn, num) { // 已知fn返回的promise函数
let time = num;
recursion(fn);
}
// 这里是一个涉及到一个重复逻辑,所以需要构建一个递归函数
const recursion = function(fn) {
fn()
.then(() => {})
.catch((err) => {
if (time > 0) {
time--;
recursion(fn)
} else {
//
}
})
}
这里有一个问题,就是这里的递归条件不满足后,应该怎么通知getNum后面的执行, 例如:
// 1.原来getNum的调用是
getNum().then(() => {
// 最开始只执行一次,成功时的回调: 逻辑1
}).catch(() => {
// 最开始只执行一次,失败时的回调: 逻辑2
})
// 2.用户希望执行过retry包装时候,是不影响后面的逻辑功能的执行的,例如:
retry(getNum).then(() => {
// 执行3次重复请求后,成功时的回调:还是之前的逻辑1
})
.catch(() => {
// 执行3次重复请求后,失败时的回调:还是之前的逻辑2
})
从包装来看,我们的retry函数不能影响到函数本身的输出,也就不影响到以前getNum之后的处理逻辑。那么我们要做的就是,retry几次之后,如果成功了就返回成功的promise,且value还是一样,失败了就返回reject的promise,err也是一样的。 有两个办法,一个是是通过then返回promise来代替,这里的问题就在于,怎么样将之前fn的成功和失败的value和状态都通过一个新的promise返回体现出来,上面的一种办法是通过then返回新的promise的状态和value体现。
function retry(fn, time) {
return recursion(fn);
}
recursion(fn) {
return Promise.resolve().then(() => {
return fn()
.then((res_value) => {
return Promise.resolve(res_value)
})
.catch((rej_value) => {
if (time > 0) {
time--;
return recursion(fn)
} else {
return Promise.reject(rej_value);
}
})
})
}
retry(getNum).then(() => {
// 执行3次重复请求后,成功时的回调:还是之前的逻辑1
})
.catch(() => {
// 执行3次重复请求后,失败时的回调:还是之前的逻辑2
})
另一个利用return promise的状态会同步上一层的promise,也就是说是通过最外层返回的promise,然后将resove函数或者reject函数通过传参数的形式放到下面fn的then函数的回调中
function retry(fn, num) {
let time = num;
return new Promise((resolve, reject) => {
// 这里是在new promise中的构造函数阶段,执行操作,直接执行后返回这个promise
// 这里返回promise是肯定的,new promise中的中心逻辑是什么情况下执行resove,什么情况下执行reject
recursion(fn);
const recursion = function(fn) {
fn().then((value) => {
resolve(value)
})
.catch((err) => {
time--;
if (time > 0) {
recursion(fn)
} else {
reject(err); // 超过执行次数就执行reject
}
})
}
})
}