使用Generator实现队列类遇问题:yield未等待Promise resolve
问题原因解析
你这段代码的核心问题在于Generator本身不会自动等待Promise完成,而且你用普通的for...of遍历Generator时,只是同步取出每个yield产出的Promise对象,根本不会等待这些Promise resolve,所以所有的广播任务会被一次性启动,完全达不到串行队列的效果。
具体来说:
processQueue是普通Generator函数,它每次yield的是runBroadcast返回的Promise,但Generator不会暂停等待这个Promise完成,只是把Promise抛出去就继续执行下一次循环了。run方法里的for...of是同步遍历,拿到每个Promise后直接打印,不会等它resolve,自然也就实现不了串行执行的队列逻辑。
修复方案
要实现串行的Promise队列,你需要结合async/await来处理Generator产出的Promise,或者直接使用异步Generator配合for await...of循环。
方案一:手动迭代Generator并await每个Promise
修改run方法为async函数,手动控制Generator的迭代过程,每次拿到Promise后等待它resolve,再继续下一个:
class BroadcastQueueHandler { #broadcastQueue; #metaData; constructor() { this.#broadcastQueue = []; } setItems(aBroadcasts) { this.#broadcastQueue = aBroadcasts; } setMetaData(oMetaData) { this.#metaData = Object.assign({}, oMetaData); } runBroadcast(oBroadcast) { const oThis = this; return new Promise((resolve, reject) => { const aBroadcasts = [oBroadcast]; const oBroadcastHandler = BroadcastHandler.factory(aBroadcasts); if (oBroadcastHandler !== null) { const oMetaData = Object.assign({}, oThis.#metaData); let fOnClick = function () {}; if (typeof oMetaData.onClick === 'function') { fOnClick = oMetaData.onClick; } oMetaData.onClick = function () { fOnClick(); resolve(oBroadcast); }; oBroadcastHandler.setItems(aBroadcasts); oBroadcastHandler.setMetaData(oMetaData); oBroadcastHandler.run(); } }); } *processQueue() { for (const oBroadcast of this.#broadcastQueue) { yield this.runBroadcast(oBroadcast); } } async run() { const generator = this.processQueue(); let result = generator.next(); while (!result.done) { // 等待当前Promise resolve后再执行下一个 await result.value; result = generator.next(); } } }
方案二:使用异步Generator + for await...of
把processQueue改成异步Generator函数(async function*),然后在run里用for await...of遍历,自动等待每个Promise完成:
class BroadcastQueueHandler { #broadcastQueue; #metaData; constructor() { this.#broadcastQueue = []; } setItems(aBroadcasts) { this.#broadcastQueue = aBroadcasts; } setMetaData(oMetaData) { this.#metaData = Object.assign({}, oMetaData); } runBroadcast(oBroadcast) { const oThis = this; return new Promise((resolve, reject) => { const aBroadcasts = [oBroadcast]; const oBroadcastHandler = BroadcastHandler.factory(aBroadcasts); if (oBroadcastHandler !== null) { const oMetaData = Object.assign({}, oThis.#metaData); let fOnClick = function () {}; if (typeof oMetaData.onClick === 'function') { fOnClick = oMetaData.onClick; } oMetaData.onClick = function () { fOnClick(); resolve(oBroadcast); }; oBroadcastHandler.setItems(aBroadcasts); oBroadcastHandler.setMetaData(oMetaData); oBroadcastHandler.run(); } }); } // 改为异步Generator函数 async *processQueue() { for (const oBroadcast of this.#broadcastQueue) { // 等待当前任务完成后,再产出结果并执行下一个 yield await this.runBroadcast(oBroadcast); } } async run() { // 使用for await...of遍历异步Generator,自动等待每个Promise完成 for await (const result of this.processQueue()) { console.log('result', result); } } }
这两种方案都能实现串行执行队列中的每个广播任务,只有当前任务的Promise resolve后,才会启动下一个任务。
内容的提问来源于stack exchange,提问作者Christoph
相关产品推荐
相关产品推荐

