先记住这个答案
yield 是「交出一个值,先停一下」,next() 返回 { value, done: false },生成器停在这里,下次 next() 从这里接着执行。return 是「交出最后一个值,彻底结束」,next() 返回 { value, done: true },生成器进入完成状态。return 之后可以继续调用 next(),不会报错,但函数体不会再执行,每次都返回 { value: undefined, done: true }。
yield暂停,done: false,还能继续return结束,done: true,不能再恢复- 结束后再
next()得到{ value: undefined, done: true } for...of和展开运算符都拿不到return的值
逐次调用 next 看区别
手动调用 next(),每次的返回值都能看得很清楚。
yield 加一个 returnJavaScriptfunction* steps() {
yield '第一步'
yield '第二步'
return '完成'
}
const it = steps()
console.log(JSON.stringify(it.next())) // 转成 JSON 看得更清楚
console.log(JSON.stringify(it.next()))
console.log(JSON.stringify(it.next()))
const after = it.next() // 已经结束,再调一次
console.log(after.value, after.done)
console.log([...steps()].join(',')) // 展开拿不到 return 的值查看输出与解释
{"value":"第一步","done":false}
{"value":"第二步","done":false}
{"value":"完成","done":true}
undefined true
第一步,第二步前两次 next() 遇到 yield,done 是 false。第三次遇到 return,value 是 '完成',done 是 true。第四次生成器已经结束,value 是 undefined,done 仍然是 true,不会报错,也不会从头再来。
最后一行用展开运算符,只拿到了两个 yield 的值,return 的 '完成' 被丢掉了。
为什么 for...of 拿不到 return 的值
for...of、展开运算符、Array.from 都按同一个规则消费迭代器:done 为 false 时,value 是一个元素,放进结果里;一旦 done 为 true,就说明遍历结束,这次的 value 不算元素,直接停止。
return值该怎么用return的值更像是「任务的最终结果」,而不是序列里的一项。比如一个生成器按步骤处理数据,每一步yield进度,最后return汇总结果。调用方想拿到汇总结果,只能手动调用next(),读到done: true那一次的value。yield*可以拿到return的值- 在一个生成器里用
const result = yield* inner()委托给另一个生成器时,inner的所有yield会被转发出去,而inner的return值会成为yield*表达式的结果,赋给result。这是return值在生成器之间传递的主要方式。
容易答错的地方
- 以为
for...of会包含return的值 for...of看到done: true就停止,不会执行循环体。需要这个值时要手动读取,或者把它改成yield。- 以为结束后再
next()会报错或重新开始 - 结束后调用
next()是安全的,只会一直返回{ value: undefined, done: true }。它不会自动重新开始,要重来只能重新调用生成器函数。
面试官还会怎么问?
没写 return 时最后一次 next() 返回什么?
函数体执行到末尾相当于 return undefined,最后一次返回 { value: undefined, done: true }。
return 后面的 yield 还会执行吗?
不会。return 之后生成器就结束了,后面的代码都不会执行。唯一的例外是 return 写在 try 里,而 finally 里还有 yield,这时会先停在 finally 的 yield 上,直到 finally 跑完才真正结束。
yield 和 await 有什么区别?
yield 把控制权交给调用 next() 的代码,什么时候继续由调用方决定,而且能交出值。await 等待一个 Promise,Promise 完成后由事件循环自动恢复执行,不会向外交出值。async 函数可以看作一个自动调用 next 的生成器。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。