如何实现一个Request/Promise请求池?

如何实现一个Request请求池,最多同时执行3个任务,超过3个任务就会进入阻塞队列,不管任务是否阻塞,都返回一个Promise对象

PS: 这是最近面试时遇到的题目,是否有比较简单的方法,能够在面试中快速手写的??

class RequestPool {
    /* 返回一个Promise对象 */
    request(url) {
        // TODO
    }
}

/* 调用方法如下 */
const pool = new RequestPool();
pool.request('1.com').then(() => {
    console.log('任务1.com执行成功');
});
pool.request('2.com').then(() => {
    console.log('任务2.com执行成功');
});
pool.request('3.com').then(() => {
    console.log('任务3.com执行成功');
});
pool.request('4.com').then(() => {
    console.log('任务4.com执行成功');
});
pool.request('5.com').then(() => {
    console.log('任务5.com执行成功');
});
pool.request('6.com').then(() => {
    console.log('任务6.com执行成功');
});
pool.request('7.com').then(() => {
    console.log('任务7.com执行成功');
});
阅读 4.1k
3 个回答

简单地写了一个 PromisePool

function randomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

async function randomAsync(threadId, serial) {
    const timeout = randomInt(2, 10) * 100;
    return new Promise(resolve => setTimeout(
        () => {
            console.log(`[${threadId} | ${serial}] ${timeout}`);
            resolve(timeout);
        },
        timeout
    ));
}

class PromisePool {
    #queue = [];
    #ids;
    #poolSize;
    #activeCount = 0;
    constructor(poolSize = 3) {
        this.#poolSize = poolSize;
        this.#ids = [...Array(poolSize).keys()];
    }

    invoke(asyncFn) {
        let _resolve, _reject;
        const promise = new Promise((resolve, reject) => {
            _resolve = resolve;
            _reject = reject;
        });
        this.#queue.push(async (...args) => {
            try {
                _resolve(await asyncFn(...args));
            } catch (err) {
                _reject(err);
            } finally {
                _resolve = _reject = null;
            }
        });
        this.#next();
        return promise;
    }

    #next() {
        if (this.#activeCount >= this.#poolSize) { return; }
        if (!this.#queue.length) { return; }

        const asyncFn = this.#queue.shift();
        this.#activeCount++;
        const threadId = this.#ids.shift();
        (async () => {
            try {
                await asyncFn?.(threadId);
            }
            finally {
                this.#activeCount--;
                this.#ids.push(threadId);
                this.#next();
            }
        })();
    }
}

(async () => {
    const pp = new PromisePool();
    for (let i = 0; i < 20; i++) {
        const r = await pp.invoke((threadId) => randomAsync(threadId, i));
        console.log("got result", r);
    }
})();

为了验证输出,特意加了threadId。下面是其中一次测试的输出:

[1 | 1] 200
[1 | 3] 300
[1 | 4] 200
[0 | 0] 900
[2 | 2] 900
[1 | 5] 700
[2 | 7] 800
[0 | 6] 900
[0 | 10] 200
[1 | 8] 700
[2 | 9] 500
[2 | 13] 300
[1 | 12] 500
[0 | 11] 700
[2 | 14] 200
[2 | 17] 600
[0 | 16] 700
[1 | 15] 800
[2 | 18] 300
[0 | 19] 900

按这个画成图,是这个样子

image.png

再来一个 pool size 为 5 的结果示意

image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题