原生JavaScript中yield生成器与递归结合的实现问题咨询
让我来帮你拆解这段代码的问题,以及如何修复它:
你的代码存在的核心问题
- 递归生成器没被正确委托:在
atGet里递归调用atGet(n)时,你只是调用了这个异步生成器函数,但没有用yield*把它的产出值传递给当前生成器的调用者。这就导致递归产生的那些yield n根本没被外层的for await...of循环捕获,所以只输出了第一次的0。 - 全局变量
n的状态污染:把n放在全局作用域,会导致多次调用生成器时状态混乱,而且生成器本身应该保持自身的独立状态,不该依赖全局变量。 - offset参数逻辑混乱:初始的
offset = true设置比较生硬,递归时传递的n和offset的语义不匹配,容易造成逻辑混淆。
修复后的基础版本代码
async function* atGet(current = 0) { // 先产出当前数值,直到达到上限 if (current < 5) { yield current; // 用yield*委托递归生成器,把递归的产出值传递出去 yield* atGet(current + 1); } } const init = async () => { try { for await (let n of atGet()) { console.log(n); // 会依次输出 0,1,2,3,4 } } catch(err) { throw err; } }; init();
修复点说明
- 替换全局变量为参数:把
current作为生成器的参数,每次递归传递递增后的数值,这样每个生成器实例都有自己的状态,不会互相干扰。 - 用
yield*委托递归:yield*是专门用来委托其他可迭代对象(包括生成器)的语法,它会把委托对象的所有产出值逐个传递给当前生成器的调用者,这样递归的所有结果都能被for await...of捕获到。 - 简化逻辑:去掉了容易混淆的
offset参数,直接通过参数传递当前计数,逻辑更清晰。
结合Promise的真实场景示例
如果你的实际代码里需要处理异步操作(比如接口请求),可以参考这个版本:
async function* atGet(current = 0) { if (current < 5) { // 模拟异步请求,比如调用后端接口获取数据 const result = await new Promise(resolve => { setTimeout(() => resolve(`数据${current}`), 200); }); yield result; // 继续递归委托 yield* atGet(current + 1); } } const init = async () => { try { for await (let item of atGet()) { console.log(item); // 每隔200ms依次输出 数据0,数据1,数据2,数据3,数据4 } } catch(err) { console.error('迭代出错:', err); } }; init();
内容的提问来源于stack exchange,提问作者Horai Nuri
相关产品推荐
相关产品推荐

