使用async/await实现JS/TS异步有界队列的阻塞逻辑问题咨询
用Async/Await实现异步有界队列的阻塞逻辑
嘿,你这个需求完全可以用async/await结合Promise实现,根本不需要额外的协程!其实你要的“阻塞”本质上是异步等待——让enqueue/dequeue在条件不满足时暂停执行,直到队列状态符合要求,而这正是Promise+async/await擅长的场景。
核心思路
JavaScript里的async/await本身就是基于Promise的语法糖,而Promise是事件循环机制下的异步处理方案,不需要依赖协程就能实现这种“等待”逻辑——所谓的“阻塞”其实是让函数暂停在await处,释放事件循环去处理其他任务,等条件满足后再继续执行。
我们只需要给AsyncQueue类添加两个队列,用来保存等待中的Promise的resolve函数:
waitersEnqueue:当队列满时,enqueue的请求会在这里等待,直到有元素被dequeue后触发resolvewaitersDequeue:当队列空时,dequeue的请求会在这里等待,直到有元素被enqueue后触发resolve
修改后的完整代码
class AsyncQueue<T> { private queue: T[] = []; private maxSize: number = 1; // 保存等待入队的Promise解析函数 private waitersEnqueue: (() => void)[] = []; // 保存等待出队的Promise解析函数 private waitersDequeue: (() => void)[] = []; async enqueue(x: T) { // 循环检查队列是否已满,直到有可用空间 while (this.queue.length >= this.maxSize) { await new Promise<void>((resolve) => this.waitersEnqueue.push(resolve)); } this.queue.unshift(x); // 如果有等待出队的任务,唤醒第一个等待的请求 if (this.waitersDequeue.length > 0) { const resolve = this.waitersDequeue.shift(); resolve!(); } } async dequeue(): Promise<T> { // 循环检查队列是否为空,直到有元素可取 while (this.queue.length === 0) { await new Promise<void>((resolve) => this.waitersDequeue.push(resolve)); } const item = this.queue.pop()!; // 如果有等待入队的任务,唤醒第一个等待的请求 if (this.waitersEnqueue.length > 0) { const resolve = this.waitersEnqueue.shift(); resolve!(); } return item; } } async function produce<T>(q: AsyncQueue<T>, x: T) { await q.enqueue(x); } async function consume<T>(q: AsyncQueue<T>): Promise<T> { return await q.dequeue(); } // 测试代码:先输出3、4,1秒后输出5、6 (async () => { const q = new AsyncQueue<number>(); consume(q).then(console.log); // 等待元素,拿到3 consume(q).then(console.log); // 等待元素,拿到4 produce(q, 3); produce(q, 4); // 这两个consume会阻塞,直到有新元素入队 consume(q).then(console.log); consume(q).then(console.log); // 延迟添加新元素测试后续等待逻辑 setTimeout(() => { produce(q, 5); produce(q, 6); }, 1000); })();
关键逻辑解释
- 等待队列的维护:当队列状态不满足操作条件时,我们创建一个未resolve的Promise,把它的resolve函数存入对应等待队列,然后
await这个Promise——这会让当前的enqueue/dequeue函数暂停执行。 - 状态变化的唤醒:每当成功完成
enqueue或dequeue操作后,检查对应等待队列是否有等待的任务,如果有就取出第一个resolve函数执行,让对应的暂停函数继续运行。 - 用while循环而非if:因为有可能在Promise被resolve后,队列状态又不满足条件了(比如多个生产者同时被唤醒,但队列又满了),所以需要再次检查队列长度,确保条件满足后再执行操作。
结论
仅用async/await(结合Promise)就完全能实现你要的异步有界队列阻塞逻辑,不需要额外的协程。这种实现方式是非阻塞的,不会卡住事件循环,在Node.js和浏览器环境都能正常工作。
内容的提问来源于stack exchange,提问作者Hugo Sereno Ferreira
相关产品推荐
相关产品推荐

