如何用JavaScript Generator实现等待回调执行后再迭代的功能?
我一直尝试找解决方案,但写简单示例都没成功。我有如下格式的数组:
const arr = [{ "content": "FIRST_CONTENT_HERE", "callback": function callback () {} }, { "content": "SECOND_CONTENT_HERE", "callback": function callback () {} }, { "content": "THIRD_CONTENT_HERE", "callback": function callback () {} }, { "content": "THIRD_CONTENT_HERE", "callback": function callback () {} } ];
我想创建一个Generator来遍历这个数组,但必须等当前元素的callback执行完成后,才进入下一次迭代。我试过把callback包装在Promise里,让它只在callback执行后才resolve;也试过把callback设为async函数来包装Promise,但都没成功——调用Generator后会直接遍历完所有数组元素,完全不会等待callback执行。
我查了相关资料,但大多数Generator和Promise/async函数结合的示例都是类似yield Promise.resolve(x)的形式,这不是我需要的——我不需要立即resolve,而是要等作为弹窗关闭onclick处理器的callback触发后才继续。也就是前一个弹窗关闭后,才实例化下一个弹窗。每轮迭代需要完成以下步骤:
- 实例化一个弹窗
- 为该弹窗设置onclick处理器,其中会执行
callback() - 将callback包装为返回Promise的函数,仅在事件触发时才resolve,同时执行callback
- Promise resolve后,Generator进入下一次迭代,实例化下一个弹窗,直到遍历完数组
我不确定自己的思路对不对,但我觉得Generator和Promise是可行的方向。请问该怎么实现这一功能?
以下是我尝试的代码(为验证概念做了多次修改,用fetch是因为它返回Promise):
async function runRequest(url) { const response = await fetch(url); return response; } async function* setupGenerator(arr) { for (let i = 0; i < arr.length; i++) { const response = await runRequest(arr[i]); yield response; } } const urls = [ '/activityViewer/index.html', '/message/index.html', '/users/index.html', ]; debugger; const generator = setupGenerator(urls); for (let response of generator) { console.log('In FOR loop; response; ', response); }
你的核心思路是正确的,问题出在两个关键点:一是普通for...of无法处理异步Generator,需用for await...of;二是没有正确将弹窗关闭的用户交互事件包装为Promise,让Generator等待事件触发。
1. 封装弹窗与等待逻辑
先写一个工具函数,创建弹窗并返回Promise,仅在弹窗关闭(触发callback)时resolve:
function createPopupAndWait(content, callback) { return new Promise((resolve) => { // 模拟弹窗创建(替换为你实际的弹窗逻辑) const popup = document.createElement('div'); popup.textContent = content; popup.style.padding = '20px'; popup.style.border = '1px solid #ccc'; popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; document.body.appendChild(popup); // 添加关闭按钮 const closeBtn = document.createElement('button'); closeBtn.textContent = '关闭'; popup.appendChild(closeBtn); // 绑定关闭事件:执行callback -> 移除弹窗 -> 触发Promise resolve closeBtn.addEventListener('click', () => { callback(); document.body.removeChild(popup); resolve(); }); }); }
2. 编写异步Generator
遍历数组时,等待当前弹窗的Promise完成后再进入下一轮迭代:
async function* popupGenerator(arr) { for (const item of arr) { await createPopupAndWait(item.content, item.callback); // 可根据需求yield当前迭代的结果,比如内容或索引 yield `处理完成:${item.content}`; } }
3. 执行Generator
用for await...of遍历异步Generator,确保每一步都等待前一个弹窗关闭:
async function runPopups() { const arr = [ { content: '第一个弹窗', callback: () => console.log('第一个弹窗关闭') }, { content: '第二个弹窗', callback: () => console.log('第二个弹窗关闭') }, { content: '第三个弹窗', callback: () => console.log('第三个弹窗关闭') } ]; const generator = popupGenerator(arr); for await (const result of generator) { console.log(result); } } // 启动执行 runPopups();
关键说明
- 异步Generator与
for await...of:普通for...of无法识别异步Generator的yield值,必须用for await...of才能等待每个Promise完成。 - Promise封装用户交互:将弹窗关闭的点击事件作为Promise的resolve时机,确保Generator会等待用户操作完成后再继续。
- callback执行顺序:先执行传入的
callback再resolve Promise,保证callback完成后才进入下一轮迭代。
内容的提问来源于stack exchange,提问作者Christoph

