由一道面试题引出的前端永动机问题

最近有看到这样一道面试题。
 
大概意思是一个promise管理组件,所有的对外的请求都走该组件。然后要求该组件对请求进行管理。实现的效果是同一时刻只能有2个请求在进行,不满2个就会发出新的请求。
这道题一开始我的思路是肯定有一个promise的收集器,然后对收集的promise进行管理,一开始肯定要发出2个请求,然后关键是一个请求结束之后发出新的请求,保证2个请求一直在进行中。我是通过promise的trace发出2个请求的,然后只要有一个返回就再发出一个新的请求。
 
但是显然我的这个思路有一个问题:解决了需求的发出,只解决了发出;没有注意到需求一直进来的,按照我的思路是无法实现对进来需求的管理,只是一个一次性的发出方案。
 
后来借鉴了一些思路。然后明白了该怎么解决。
 
其实这个组件里面是需要有一套独立的逻辑的,这套逻辑就是用来处理进来的请求以及保证2个请求同时进行。外部显然是持续进来,既然是持续进来内部要一套类似事件循环的机制,类似一种待机状态,只要满足需求就开始运转起来。
 
我发现这种机制有点类似永动机。只不过是一种有条件的永动机。在前端实现永动机的机制有两种方法:一种利用settimeout、setInterval;另一种是利用递归。
 
后来我找到了一种利用递归实现上述组件的方案:
 
   class Scheduler {
            constructor() {
                this.queue = []; // 队列
                this.maxCount = 2; // 最大并行数
                this.runCounts = 0; // 请求中的数量
            }
            add(promiseCreator) {
                this.queue.push(promiseCreator);
            }
            taskStart() {
                for (let i = 0; i < this.maxCount; i++) {
                    this.request();
                }
            }
            request() {
                if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
                    return;
                }
                this.runCounts++;

                this.queue.shift()().then(() => {
                    this.runCounts--;
                    this.request();
                });
            }
        }

        const timeout = time => new Promise(resolve => {
            setTimeout(resolve, time);
        })

        const scheduler = new Scheduler();

        const addTask = (time, order) => {
            scheduler.add(() => timeout(time).then(() => console.log(order)))
        }


        addTask(1000, '1');
        addTask(500, '2');
        addTask(300, '3');
        addTask(400, '4');

        scheduler.taskStart()
        setTimeout(() => {
            addTask(1000, '5');
            addTask(500, '6');
            addTask(300, '7');
            addTask(400, '8');
        }, 2000);
 
然后自己写了一个利用setInterval实现的方案:
 
  class Scheduler2 {
            constructor() {
                this.queue = []; // 队列
                this.maxCount = 2; // 最大并行数
                this.runCounts = 0; // 请求中的数量
                this.setInter = null
            }
            add(promiseCreator) {
                this.queue.push(promiseCreator);
            }
            taskStart() {
                for (let i = 0; i < this.maxCount; i++) {
                    this.request();
                }
                this.setInter = setInterval(() => {
                    console.log('uuuu')
                    this.request();
                }, 0);
            }
            request() {
                if (!this.queue.length || this.runCounts >= this.maxCount) {
                    return;
                }
                this.runCounts++;

                this.queue.shift()().then(() => {
                    this.runCounts--;
                });
            }
            destroy() {
                clearInterval(this.setInter)
            }
        }
        const timeout = time => new Promise(resolve => {
            setTimeout(resolve, time);
        })

        const scheduler = new Scheduler2();

        const addTask = (time, order) => {
            scheduler.add(() => timeout(time).then(() => console.log(order)))
        }


        addTask(1000, '1');
        addTask(500, '2');
        addTask(300, '3');
        addTask(400, '4');

        scheduler.taskStart()

        setTimeout(() => {
            addTask(1000, '5');
            addTask(500, '6');
            addTask(300, '7');
            addTask(400, '8');
        }, 1000);
但我后来想到一个问题,第一个实现方案是已经确定了promise进来的数量。我发现如果我2秒之后添加的新的请求,第一个就不在发挥作用了。换句话说第一种方案添加进请求会有一个时间上限,超过这个时间上限就失效了。
 
而setInterval在不考虑清除setInterval的情况下真正实现了一旦开启想什么时候进来都行。然后setInterval需要在组件失效时候清除就好了。
 
这道题给我的启示是:对复杂场景的开发解决方案给出还不足,对开发内在的一些逻辑还能够更好的去应用,可以主动找一些复杂的开发场景学习学习。

我站在山顶看风景!下面是我的家乡!
原文地址:https://www.cnblogs.com/zhensg123/p/14618339.html