likes
comments
collection
share

任务队列的 JS 实现

作者站长头像
站长
· 阅读数 56

引言

假设有这么一个场景:

  • 前端订阅后台数据的变化,如果发生变化,则触发订阅回调;
  • 回调函数中,会执行一些耗时操作,如:请求接口,发送短信,存历史数据等;
  • 要求以上所有的操作都必须按照订阅触发的顺序执行;

我们都知道,回调本身就是一种异步操作,我们仅仅依靠订阅回调无法保证回调中任务执行顺序的。

为了解决这个问题,我们可以使用任务队列,将回调函数添加到任务队列中,然后按照顺序依次执行。

任务队列的概念

顾名思义,任务队列就是存放任务的队列,队列中的任务都严格按照进入队列的先后顺序执行。

在前一条任务执行完毕后,立即执行下一条任务,直到任务队列清空。

任务队列的基本执行流程如下:

  • 给任务队列添加任务,并判断执行标识;
  • 如果执行标识为 false,则将任务从队列中独立出来;
  • 开始执行任务,将执行标识置为 true;
  • 执行完毕后,判断队列是否为空,如果不为空,则继续执行下一条任务;
  • 直至任务队列清空。

任务队列一直在循环进行如上步骤。

任务队列的 JS 实现

我们选择使用数组来维护队列的执行顺序,按照“先进先出“的原则,依次从数组的第一个元素开始往后执行。

简易代码实现如下:

/** @format */

class TaskQueue {
    constructor() {
        this.queue = [];
        this.isRunning = false;
    }

    /**
     * @description 将任务添加至队列
     * @param {Promise} queueFunction
     * @returns {undefined}
     */
    addQueue(queueFunction) {
        this.queue.push(queueFunction);
        if (!this.isRunning) {
            this.processQueue();
        }
    }

    /**
     * @description 如果队列不为空,则开始处理队列中的任务,本次任务执行完毕后,立刻开始下一条任务的执行
     * @param {}
     */
    processQueue() {
        if (this.queue.length > 0) {
            this.isRunning = true;
            const queueFunction = this.queue.shift();
            queueFunction().finally(() => {
                this.processQueue();
            });
        } else {
            this.isRunning = false;
        }
    }
}

测试

我们使用如下代码来测试上述实现:

let taskQueue = new TaskQueue();

let f1 = function (name) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`我是 f1`, name);
            resolve();
        }, 3000);
    });
};

let f11 = async function () {
    await f1("    任务 1");
};

let f2 = function (name) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`我是 f2`, name);
            resolve();
        }, 2000);
    });
};

let f22 = async function () {
    await f2("    任务 2");
};

let f3 = function (name) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`我是 f3`, name);
            resolve();
        }, 1000);
    });
};

let f33 = async function () {
    await f3("    任务 3");
};

taskQueue.addQueue(f11);
taskQueue.addQueue(f22);
taskQueue.addQueue(f33);

// 依次输出:
// 我是 f1     任务 1
// 我是 f2     任务 2
// 我是 f3     任务 3

如果不使用任务队列,上面测试的打印结果应该是按照 f3 -> f2 -> f1 的顺序执行的结果,但是使用了任务队列之后,就会严格按照f1 -> f2 -> f3 的顺序执行,这是因为任务队列的执行顺序是按照添加的顺序来的,而不是按照执行顺序来的。

总结

任务队列的实现非常简单,但是使用起来却非常灵活,我们可以根据实际需求来决定任务队列的执行顺序,也可以根据实际需求来决定任务队列的执行时机。

本文算是抛砖引玉,介绍了简洁版的任务队列实现,如果大家有更好的想法和建议,都可以在评论区留言,一起讨论。

~

~ 本文完,感谢阅读!

~

转载自:https://juejin.cn/post/7270829887517491241
评论
请登录