美文网首页
前端面试题-实现一个封装的ajax器

前端面试题-实现一个封装的ajax器

作者: WEB前端含光 | 来源:发表于2020-06-28 22:11 被阅读0次

实现一个封装ajax器,功能有

限制一次同时发送的ajax请求数量m个
timeout限制
重试n次
解题思路
强调下,我的想法和代码只是尝试回答面试问题,并不能直接在正式的业务场景里使用,但希望也能给你们带去思考。另外,我的解决方案是基于promise完成的。
首先,限制条件1在我的理解中,发送的请求还有m个请求在请求中,那么这时程序发出的请求,要先被存下来,等到有请求结束了,再发送给服务器。这就需要我们思考用什么结构,存储请求,考虑到请求是先进先出的,那么就可以使用队列。
其次,2、3这两个限制条件,合起来说就超时重试了,重试了n次后,就当这个请求失败了,不再尝试。这个超时重试如何实现呢?我的理解是,发送一次后,加个定时器,当超时后,就会执行定时器里的代码,取消这次请求,尝试下次请求,为这个请求的重试次数计数。这就需要实现一个可以取消的Promise。
队列
实现一个队列,一般要实现进队、出队的方法,符合先进先出的原则,同时也要实现查询队满、队空的状态。 因为如果进出队次数过多会造成数组过大,为了不造成浪费,这里就实现的是循环队列。

//实现一个队列
class Queue{
    constructor(size){
        this.size = size;
        this.data = Array(size);
        this.front = 0;
        this.rear = 0;
    }

    in(url){
        if(this.isFull()) return false;
        this.data[this.rear] = url;
        this.rear = (this.rear+1)%this.size;
        return true;
    }

    out(){
        let res = null;
        if(this.isEmpty()) return res;
        res =  this.data[this.front];
        this.data[this.front] = null;
        this.front = (this.front+1)%this.size;
        return res;
    }

    isFull(){
        return this.front===this.rear&&this.data[this.size-1];
    }

    isEmpty(){
        return this.front===this.rear&&!this.data[this.size-1];
    }

}
复制代码

可取消的Promise
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

我们利用的就是race方法返回一个被包装的Promise,我们基于这个做回调处理,里面有用来取消的Promise,和原始的Promise,当需要取消Promise,用来取消的Promise resolve就可以了。

//返回可以取消的Promise
class CanCancelPromise{

    constructor(promise){
        let resolve,reject,cancelP;
        if(promise instanceof Promise){
            cancelP = new Promise(function(res, rej){
                resolve = res;
                reject = rej;
            });

            this.promise = Promise.race([promise, cancelP]);

            this.cancel = ()=>{
                resolve(CancelPromise);
            };
            
        }else{
            throw new Error("请传入Promise对象")
        }
    }

}
复制代码

实现ajax封装器
当前面两部准备工作完成,就可以借助它们来实现ajax封装器了。

class axios{

    /**
     * 初始化axios
     * @param maxSize 限制同时请求的ajax请求数量m
     * @param reTryCount 重试次数
     * @param timeout 超时设定时间
     * @param poolSize 请求池的大小
     */
    constructor(maxSize=3,reTryCount=3,timeout = 100,poolSize=100){
        this.requests = new Queue(poolSize);
        this.maxSize = maxSize;
        this.curSize = 0;
        this.timeout = timeout;
        this.reTryCount = reTryCount;
    }

    //传入请求url和回调函数
    post(url,handle=null){
        if(this.requests.in(url)){
            return this.tryRequestUrl(handle);
        }else{
            throw new Error("请求池已满");
        }
    }

    //存入请求池,开始根据最大请求判断是否发出请求
    tryRequestUrl(handle){
        if(this.curSize<this.maxSize){
            let url = this.requests.out();
            if(url){
                console.log(`开始连接${url}`);
                this.reTryRequest(url,handle,this.timeout,this.reTryCount);
                this.curSize ++;
            }
        }else{
            console.log(`等待连接`);
        }
    }


    // 请求并负责重试
    reTryRequest(url,handle, wait,num) {
        let timeout, i=1;
    
        let request = (url)=>{
            //模拟url请求,现实场景中可以fetch代替
            //这里的请求很容易超时
            let promiseObj = new CanCancelPromise(new Promise((resolve, reject) => {
                setTimeout(resolve, Math.random()*1000);
            }));

            promiseObj.promise.then((value)=>{
                if(value===CancelPromise){
                    console.log("取消成功");
                    return;
                }
                if(timeout){
                    clearInterval(timeout);
                    timeout = null;
                }
                handle(value);
                console.log(`请求${url}第${i-1}次成功`);
                this.curSize--;
                this.tryRequestUrl();
                i = num;
            });
            return promiseObj;
        };
    
        let pObj = request(url);
        //负责重试
        timeout = setInterval(() => {
            pObj.cancel();
            if(i===num){
                clearInterval(timeout);
                timeout = null;
                console.log(`请求${url}重试${i}次失败,不再重试!`);
            }else{
                pObj = request(url);
                console.log(`请求${url}重试${i}次失败`);
                this.curSize--;
                this.tryRequestUrl();
            }
            i++;
        }, wait);
    }

}
复制代码

测试代码

let a = new axios();
a.post("baidu1.com");
a.post("baidu2.com");
a.post("baidu3.com");
a.post("baidu4.com");
a.post("baidu5.com");
a.post("baidu6.com");
复制代码

有想了解更多的小伙伴可以加Q群链接里面看一下,应该对你们能够有所帮助。

相关文章

网友评论

      本文标题:前端面试题-实现一个封装的ajax器

      本文链接:https://www.haomeiwen.com/subject/ahmgfktx.html