提问人:Xaree Lee 提问时间:12/26/2016 更新时间:11/12/2023 访问量:13862
JS:如何在回调中使用生成器和产量
JS: how to use generator and yield in a callback
问:
我使用JS生成器在回调中生成一个值:setTimeout
function* sleep() {
// Using yield here is OK
// yield 5;
setTimeout(function() {
// Using yield here will throw error
yield 5;
}, 5000);
}
// sync
const sleepTime = sleep().next()
为什么我不能在生成器的回调中生成值?
答:
function*
声明是同步的。您可以生成一个新对象,链接到以检索解析的值Promise
.then()
.next().value
Promise
function* sleep() {
yield new Promise(resolve => {
setTimeout(() => {
resolve(5);
}, 5000);
})
}
// sync
const sleepTime = sleep().next().value
.then(n => console.log(n))
.catch(e => console.error(e));
评论
yield
setInterval
.then(n => console(n))
应该是.then(n => console.log(n))
我来到这个问题是为了寻找一种方法来将每隔一段时间调用的回调(例如节点流、事件侦听器或回调)转换为异步可迭代,经过一些研究,我找到了这个 NPM 包可以完成所有这些工作:EventIterator
。setInterval
EventIterator
是一个小型模块,可大大简化将事件发射器、事件目标和类似对象转换为 EcmaScript 异步迭代器的过程。它适用于浏览器和 Node.js 环境。
一个基本的可迭代对象:setInterval
import { EventIterator } from "event-iterator"
const counter = ms =>
new EventIterator(({ push }) => {
let count = 0
const interval = setInterval(() => push(++count), ms)
return () => clearInterval(interval)
})
for await (const count of counter(1000)) console.log(count)
(想想喜欢)。push
yield
虽然这在技术上并不能回答这个问题,但公认的答案也没有真正回答它,而且这个解决方案似乎非常接近 OP 所寻找的。
评论
EventIterator
下面的代码在循环中产生。因此可以产生不止一次。
async function* foo(loopVal) {
for(let i=0;i<loopVal;i++){
yield new Promise(resolve => {
setTimeout(() => {
resolve(i);
}, 5000);
})
}
}
(async function() {
for await (const num of foo(5)) {
console.log(num);
}
})();
评论
要直接回答问题,不能使用“*”/“yield”语法来做到这一点。从这里: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
...“yield”关键字只能在包含它的生成器函数中直接使用。“它不能在嵌套函数中使用”,例如回调。
对于OP的“为什么”问题,无益的答案是,它被ECMAScript语言规范所禁止,至少在严格模式下是这样:https://262.ecma-international.org/9.0/#sec-generator-abstract-operations
关于原因的更多直觉:生成器的实现是“yield”关键字暂停其生成器函数的执行。生成器函数的执行在其他方面是普通的,当它返回时,它生成的可迭代对象结束。这向调用方发出信号,表明不再有值传入,任何等待它的循环也将结束。在那之后,即使嵌套回调再次运行,也没有机会向任何感兴趣的调用方提供任何内容。
尽管回调或其他嵌套函数可以绑定来自外部生成器的变量,但它可以逃避生成器的生存期,并在任何其他时间/地点/上下文中运行。这意味着所需的 yield 关键字可能没有要暂停的功能,也没有要为其生成值的调用者或循环。我推测,严格模式语法错误被放在这里是为了防止代码作者发生一些悄无声息的不受欢迎的事情。
也就是说,生成器语法对于创建 OP 的预期效果不是必需的。当目标只是让 “next()” 工作,或参与异步迭代器协议(“for await (...)”)时,这些协议可以符合使用普通函数和对象,而不需要屈服。您要遵守的协议记录在下面:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
...另一个提到 EventIterator 的答案是帮助程序代码的一个示例,它使这更容易实现。
评论