javascript JS:如何在回调中使用generator和yield

n8ghc7c1  于 2023-11-15  发布在  Java
关注(0)|答案(4)|浏览(111)

我使用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()

字符串
为什么我不能在生成器的回调函数中产生值?

mm5n2pyu

mm5n2pyu1#

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));

字符串

j0pj023g

j0pj023g2#

我在寻找一种方法来将一个经常调用的回调(例如Node流,事件侦听器或setInterval回调)转换为一个可迭代对象,经过一些研究,我发现了这个NPM包:EventIterator
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所寻找的。

slmsl1lt

slmsl1lt3#

要直接回答这个问题,使用“*”/“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()”工作时,或者参与到Iterator协议(“for await(...)”)中,这些协议可以使用普通的函数和对象来遵循,而不需要yield。你想要遵循的协议在这里有文档记录:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
...另一个提到EventIterator的答案是一个帮助代码的例子,它使这更容易做到。

2nbm6dog

2nbm6dog4#

扩展the guest271314's answer.
下面的代码在一个循环中产生。因此可以产生不止一次。

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);
  }
})();

字符串

相关问题