腾讯wxg笔试题:手写一个能控制最大并发数量的Promise调度器

462 阅读1分钟

面试时直接给出了三道题,这是第二道题,还有两道是树结构对象打平和实现可以决定是否可以立即执行的防抖函数。

要求

需要实现一个Scheduler类,上面有add方法,add方法接收一个promiseCreator函数,执行promiseCreator会创建一个新的Promise。需要控制同时执行的Promise数量(也也是控制什么时候执行这个promiseCreator函数)。

题目中现有的代码包括Scheduler类的基本结构(构造函数、add方法,里面的内容是空的); 一个timeout函数;一个实例化的Scheduler对象,并执行了多个add方法。

由于面试系统不熟悉,写的时候并没有调试,但当时写的应该也不对。

以下是面试后想出的实现方法,面试官当时好像希望add方法返回的Promise能够反映原始Promise的状态,但以下代码不能实现,希望各位能够指点一下:


class Scheduler {
    constructor() {
        this.queue = [];
        this.currentTask = null;
        this.running = false;
        this.maxTask = 2;
        this.runningTaskNum = 0;
    }

    add(promiseCreator) {
        // 返回一个promise,使用函数的形式保存了参数
        // 进而实现不立即创建promise
        // 而返回promise的状态也由promiseCreator 创建的promise进行控制
        return new Promise((resolve, reject) => {
            this.queue.push(() => {
                const promise = promiseCreator();
                return promise.then(resolve, reject);
            });

            if (!this.running) {
                this.run();
            }
        });
    }

    async run() {
        console.log('run')
        if (this.queue.length === 0) {
            return;
        }

        this.running = true;
        let thisTaskPoll = [];
        while (this.queue.length) {
            if (this.runningTaskNum < this.maxTask) {
                const task = this.queue.shift();
                const promise = task().finally(() => {
                    this.runningTaskNum--;
                    thisTaskPoll.splice(thisTaskPoll.indexOf(promise), 1);
                    if (this.runningTaskNum === 0) {
                        this.running = false;
                        this.run();
                    }
                });
                this.runningTaskNum++;
                thisTaskPoll.push(promise);
            }
            if (this.runningTaskNum === this.maxTask) {
                await Promise.race(thisTaskPoll);
            }
        }
    }
}

function timeout(delay) {
    return new Promise((resolve, reject) => {
        if (Math.random() < 0.3) {
            reject()
        } else {
            setTimeout(resolve, delay);
        }
    });
}


const scheduler = new Scheduler();

scheduler.add(() => timeout(1000).then(() => {
    console.log('task 1');
}).catch((e) => {
    console.log('task 1 faild')
}));
scheduler.add(() => timeout(2000).then(() => {
    console.log('task 2');
}).catch((e) => {
    console.log('task 2 faild')
}));

scheduler.add(() => timeout(0).then(() => {
    console.log('task 3');
}).catch((e) => {
    console.log('task3 faild')
}));

scheduler.add(() => timeout(0).then(() => {
    console.log('task 4');
}).catch((e) => {
    console.log('task 4 faild')
}));

scheduler.add(() => timeout(0).then(() => {
    console.log('task 5');
}).catch((e) => {
    console.log('task 5 faild')
}));
// 1 3 4 5 2
// 1 2队列中
// 1 完成 3 4 5 按次进入队列
// 最后完成2