You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现队列为空时支持同步执行的有序Promise任务队列?

解决方案

原有实现的问题点

你之前用Promise.all等待Promise数组的思路有两个核心缺陷:

  1. 等待期间新增的Promise不会被纳入当前的Promise.all,会导致后续任务比前面的Promise先执行,顺序错乱
  2. 没有合适的时机清空数组:如果在Promise.all的回调里清空,可能会把等待期间新增的未处理Promise删掉;如果不清空,数组会越来越大,isEmpty判断永远返回false,后续所有任务都会被强制变成异步

核心设计思路

完全兼顾你需要的两个核心特性:

  • 队列为空且没有处理中的异步任务时,同步任务(字符串)直接执行,完全不涉及Promise异步逻辑,保证原有单元测试100%兼容
  • 队列存在未完成的异步任务时,所有后续任务(不管同步还是异步)严格按调用顺序排队,全部执行完成后自动重置队列为空状态,后续任务恢复同步执行特性
  • 通用设计,和echo的具体实现完全解耦,可用于任意类似的任务调度场景

完整实现代码

class AsyncTaskQueue {
    constructor() {
        // 存储待处理任务
        this._pendingTasks = [];
        // 标记是否正在处理异步任务
        this._isProcessing = false;
        // 队列尾部Promise,用于保证任务执行顺序
        this._tailPromise = Promise.resolve();
    }

    /**
     * 添加任务到队列
     * @param {*} task 任务内容,支持普通值、Promise
     * @param {Function} resolveCallback 任务完成后的回调,接收任务返回值
     */
    add(task, resolveCallback) {
        // 队列为空+无处理中任务+当前是同步值:直接同步执行,完全不碰Promise逻辑
        if (this._pendingTasks.length === 0 && !this._isProcessing && typeof task?.then !== 'function') {
            resolveCallback(task);
            return;
        }

        // 否则加入待处理队列
        const taskItem = typeof task?.then === 'function'
            ? { type: 'async', value: task, callback: resolveCallback }
            : { type: 'sync', value: task, callback: resolveCallback };
        this._pendingTasks.push(taskItem);

        // 没有正在运行的处理流程就启动处理
        if (!this._isProcessing) {
            this._processQueue();
        }
    }

    /**
     * 检查队列是否完全空闲
     * @returns {boolean}
     */
    isIdle() {
        return this._pendingTasks.length === 0 && !this._isProcessing;
    }

    /**
     * 内部方法:按顺序处理队列中所有任务
     */
    _processQueue() {
        this._isProcessing = true;
        // 拷贝当前所有待处理任务,清空原队列接收后续新增任务
        const tasksToProcess = [...this._pendingTasks];
        this._pendingTasks = [];

        // 逐个将任务挂到尾部Promise后,保证严格按调用顺序执行
        for (const task of tasksToProcess) {
            this._tailPromise = this._tailPromise.then(() => {
                if (task.type === 'sync') {
                    task.callback(task.value);
                    return Promise.resolve();
                }
                return task.value.then(result => {
                    task.callback(result);
                });
            });
        }

        // 当前批次任务处理完成后检查是否有新增任务
        this._tailPromise = this._tailPromise.then(() => {
            if (this._pendingTasks.length > 0) {
                // 有新增任务,继续处理
                this._processQueue();
            } else {
                // 无新增任务,重置队列状态,恢复同步执行能力
                this._isProcessing = false;
                this._tailPromise = Promise.resolve();
            }
        });
    }
}

与echo方法集成示例

只需要在Terminal实例中初始化队列,调用时把渲染逻辑作为回调传入即可:

class Terminal {
    constructor() {
        this._taskQueue = new AsyncTaskQueue();
    }

    echo(input) {
        // 处理传入函数的场景:执行函数拿到返回值
        const task = typeof input === 'function' ? input() : input;
        // 加入队列,回调复用你原有的渲染逻辑
        this._taskQueue.add(task, (outputText) => {
            this._renderToTerminal(outputText);
        });
    }

    // 原有渲染逻辑,不需要修改
    _renderToTerminal(text) {
        const el = document.querySelector('.terminal-output');
        el.innerHTML += `<div class="line">${text}</div>`;
    }
}

场景验证

场景1:混合Promise与字符串调用

你给出的第一个测试用例执行逻辑完全符合预期:

  1. 前6个echo按调用顺序排队执行,总耗时约3s全部渲染完成,之后队列自动重置为空闲状态
  2. 5s时调用term.echo('lorem ipsum')时队列为空闲,直接同步渲染到DOM,单元测试可以立即查询到对应的DOM节点,没有任何异步延迟

场景2:全同步字符串调用

所有echo调用时队列都处于空闲状态,全部直接同步执行渲染,和原有实现行为完全一致,没有任何破坏性变更。

内容的提问来源于stack exchange,提问作者jcubic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 18:15:04