Promise相关请求

93 阅读4分钟

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
               }
           })
       }
   })
}

4.promise.cancel实现