funnycoderstar/blog

限制并发请求

Opened this issue · 0 comments

题目一

JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个;
完善下面的 Scheduler 类,使得以下程序能正确输出

class Scheduler {
    add(promise) {
        // ...
    }
}
const timout = (time) =>
    new Promise((resolve) => {
        setTimeout(resolve, time);
    });
const scheduler = new Scheduler();
const addTask = (time, order) => {
    scheduler.add(() =>
        timout(time).then(() => {
            console.log(order);
        })
    );
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');

// output: 2 3 1 4
// 一开始,1, 2两个任务进入队列
// 500ms时,2完成,输入2;任务3进队
// 800ms时,3完成,输出 3;任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4

代码实现

主要思路:

  1. 使用一个队列 queue 来存储当前执行的函数
  2. 使用一个标识 running 表示当前并发执行的数量
  3. 判断当前队列是否为空或者 当前执行的并发个数为 2,则直接 return
  4. 取出
class Scheduler {
    constructor() {
        this.queue = [];
        this.running = 0;
    }

    run() {
        if (this.queue.length === 0 || this.running === 2) {
            return;
        }
        const p = this.queue.shift();
        this.running++;
        p().then((result) => {
            this.running--;
            this.run();
            return result;
        });
    }
    add(promise) {
        this.queue.push(promise);
        this.run();
    }
}
const timout = (time) =>
    new Promise((resolve) => {
        setTimeout(resolve, time);
    });
const scheduler = new Scheduler();
const addTask = (time, order) => {
    scheduler.add(() =>
        timout(time).then(() => {
            console.log(order);
        })
    );
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');

// output: 2 3 1 4
// 一开始,1, 2两个任务进入队列
// 500ms时,2完成,输入2;任务3进队
// 800ms时,3完成,输出 3;任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4

题目二

  1. 实现一个批量请求函数 multiRequest(urls, maxNum),要求最大并发数 maxNum,
  2. 每当有一个请求返回,就留下一个空位,可以增加新的请求。
  3. 所有请求完成后,结果按照 urls 里面的顺序依次打出。
/**
 * 限制并发请求。逻辑步骤分解
1. count,存储当前执行的数据,和 MaxNum 做对比
2. 判断并发个数:count 和 maxNum 做对比
3. 并发执行请求,
4. 返回结果
 * @param {*} urls 
 * @param {*} maxNum 
 */
// 返回对应的result; 包含成功和异常的处理情况
function multiRequest(urls, maxNum) {
    return new Promise((resolve, reject) => {
        let i = 0; // 当前执行的第i个函数,需要用它来存储结果的顺序
        let peddingCount = 0; // 记录正在执行的函数个数
        let successCount = 0; // 记录已经执行完成的函数个数,用来判断是否所有的函数都已返回正确的结果
        let result = []; // 存储最后的结果
        async function run(index) {
            // 如果所有函数都已经执行完成了,就
            if (successCount >= urls.length) {
                return resolve(result);
            }
            // 判断正在执行的个数如果大于 maxNum 或者 index 越界,就停止执行
            if (peddingCount >= maxNum || index >= urls.length) {
                return;
            }
            peddingCount++;
            try {
                result[index] = await testFetch(urls[index]);
                console.log('get result =>', urls[index], index);
            } catch (e) {
                result[index] = e;
            }
            peddingCount--;
            successCount++;
            // 此时i就是上一下执行i++后的结果,所以先执行 run(i), 再执行 i++
            run(i);
            i++;
        }
        // 并发执行 maxNum 个
        while (peddingCount < maxNum) {
            run(i);
            i++;
        }
    });
}
function testFetch(ms) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(ms);
            resolve(ms);
        }, ms * 1000);
    });
}

multiRequest([1, 4, 2, 3], 2).then((res) => {
    console.log('result', res);
});

参考