JS并发任务控制

119 阅读1分钟

在javascript中,当同时需要执行多个任务或请求,但最大并发数有所限制时,可以利用该方案解决并发控制问题。

如题

// setTimeout模拟异步请求场景
function timeout(ms: number) {
    return new Promise<void>(resolve => {
        setTimeout(() => {
            resolve();
        }, ms);
    })
}

const superTask = new SuperTask();
function addTask(time: number, name: number| string) {
    superTask.add(() => timeout(time)).then(() => {
        console.log(`${name}执行完毕`);
    })
}
  • 需要实现SuperTask类,确保代码执行结果如下
addTask(10000, 1); // 10ms后输出: 1执行完毕
addTask(5000, 2); // 5ms后输出: 2执行完毕
addTask(3000, 3); // 8ms后输出: 3执行完毕
addTask(4000, 4); // 12ms后输出: 4执行完毕
addTask(5000, 5); // 15ms后输出: 5执行完毕

分析

  1. 根据打印结果,可以判断出当前最大并发任务数是2个
  2. SuperTas必须实现一个add方法,并且add方法返回的是一个promise
class SuperTask {
    threadNum: number; // 最大线程数
    runningNum: number; // 当前正在执行的任务数量
    taskQueue: Array<any>; // 任务队列
    constructor(threadNum = 2) {
        this.threadNum = threadNum;
        this.taskQueue = [];
        this.runningNum = 0;
    }
    add(task: any) {
        // 2.实现一个add方法并返回一个promise
        return new Promise((resolve, reject) => {
            // 将所有任务放进任务队列,然后按需取任务执行
            this.taskQueue.push({
                task,
                resolve,
                reject,
            });
            // 每当有任务进入队列时,都需要判断当前是否有空余线程可执行任务
            this._run();
        });
    }
    // 内部实现一个方法,当前执行任务小于可执行任务数时,从任务队列中取出任务执行
    _run() {
        while(this.runningNum < this.threadNum && this.taskQueue.length > 0) {
            // 取第一个任务
            const {task, resolve, reject} = this.taskQueue.shift();
            this.runningNum++;
            Promise.resolve(task()).then(resolve, reject).finally(() => {
                this.runningNum--;
                // 每当执行完一个任务,重新调用_run函数,执行下一个任务
                this._run();
            });
        }
    }
}

最终打印结果为

2执行完毕
3执行完毕
1执行完毕
4执行完毕
5执行完毕

完整代码示例

function timeout(ms: number) {
    return new Promise<void>(resolve => {
        setTimeout(() => {
            resolve();
        }, ms);
    })
}

class SuperTask {
    threadNum: number; // 最大线程数
    runningNum: number; // 当前正在执行的任务数量
    taskQueue: Array<any>; // 任务队列
    constructor(threadNum = 2) {
        this.threadNum = threadNum;
        this.taskQueue = [];
        this.runningNum = 0;
    }
    add(task: any) {
        return new Promise((resolve, reject) => {
            this.taskQueue.push({
                task,
                resolve,
                reject,
            });
            this._run();
        });
    }
    _run() {
        while(this.runningNum < this.threadNum && this.taskQueue.length > 0) {
            // 取第一个任务
            const {task, resolve, reject} = this.taskQueue.shift();
            this.runningNum++;
            Promise.resolve(task()).then(resolve, reject).finally(() => {
                this.runningNum--;
                this._run();
            });
        }
    }
}

const superTask = new SuperTask();
function addTask(time: number, name: number| string) {
    superTask.add(() => timeout(time)).then(() => {
        console.log(`${name}执行完毕`);
    })
}

addTask(10000, 1); // 10ms后输出: 1执行完毕
addTask(5000, 2); // 5ms后输出: 2执行完毕
addTask(3000, 3); // 8ms后输出: 3执行完毕
addTask(4000, 4); // 12ms后输出: 4执行完毕
addTask(5000, 5); // 15ms后输出: 5执行完毕