JavaScript 中的 yield 关键字是什么?

IT技术 javascript yield keyword
2021-01-27 11:16:24

我听说过 JavaScript 中的“yield”关键字,但我发现有关它的文档非常糟糕。有人可以向我解释(或推荐一个解释的网站)它的用法和用途吗?

6个回答

迟到的回答,yield现在可能每个人都知道,但一些更好的文档已经出现。

James Long 的“Javascript 的未来:生成器”中的示例改编为官方 Harmony 标准:

function * foo(x) {
    while (true) {
        x = x * 2;
        yield x;
    }
}

“当你调用 foo 时,你会得到一个具有 next 方法的 Generator 对象。”

var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16

所以yield有点像return:你得到一些回报。 return x返回 的值x,但yield x返回一个函数,该函数为您提供了一个迭代下一个值的方法。如果您有可能需要在迭代期间中断潜在内存密集型过程则很有用

@Ajedi32 是的,你说得对。Harmony 标准化了function *之间的相关性yield,并添加了引用错误(“如果在非生成器函数中出现 yield 或 yield* 表达式,则会引发早期错误”)。但是,Firefox 中的原始 Javascript 1.7 实现不需要*. 相应地更新了答案。谢谢!
2021-03-14 11:16:24
@MuhammadUmer Js 终于成为您可以实际使用的语言。这叫做进化。
2021-03-14 11:16:24
有帮助,但我猜你在function* foo(x){那里
2021-04-03 11:16:24
@RanaDeep:扩展函数语法以添加可选 *标记您是否需要它取决于您返回的未来类型。细节很长:GvR 为 Python 实现解释了它, Javascript 实现是基于它建模的。使用function *总是正确的,尽管在某些情况下比function使用略多一些开销yield
2021-04-04 11:16:24
示例很有用,但是...什么是函数 * ?
2021-04-11 11:16:24

这真的很简单,这就是它的工作原理

  • yield关键字只是帮助异步地随时暂停恢复功能
  • 此外,它有助于生成器函数返回值

以这个简单的生成器函数为例

function* process() {
    console.log('Start process 1');
    console.log('Pause process2 until call next()');

    yield;

    console.log('Resumed process2');
    console.log('Pause process3 until call next()');

    let parms = yield {age: 12};
    console.log("Passed by final process next(90): " + parms);

    console.log('Resumed process3');
    console.log('End of the process function');
}

让 _process = process();

在您调用_process.next() 之前,不会执行前两行代码,然后第一个 yield暂停该函数。恢复函数直到下一个暂停点(yield 关键字),您需要调用_process.next()

您可以认为多个产量单个函数内 javascript 调试器中的断点在您告诉导航下一个断点之前,它不会执行代码块。注意:不会阻塞整个应用程序)

但是当 yield 执行这​​个暂停和恢复行为时,它也可以 根据之前的函数返回一些结果{value: any, done: boolean}我们还没有发出任何值。如果我们探索之前的输出,它将显示相同{ value: undefined, done: false } 的值undefined

让我们深入研究 yield 关键字。您可以选择添加表达式并设置分配默认可选值(官方文档语法)

[rv] = yield [expression];

表达式:从生成器函数返回的值

yield any;
yield {age: 12};

rv : 返回传递给生成器 next() 方法的可选值

简单地,您可以使用这种机制将参数传递给 process() 函数,以执行不同的产量部分。

let val = yield 99; 

_process.next(10);
now the val will be 10 

现在就试试

用法

  • 懒惰评价
  • 无限序列
  • 异步控制流

参考:

MDN文档是相当不错的,海事组织。

包含 yield 关键字的函数是一个生成器。当你调用它时,它的形式参数绑定到实际参数,但它的主体实际上没有被评估。相反,返回一个生成器-迭代器。每次调用生成器-迭代器的 next() 方法都会执行另一次迭代算法。每个步骤的值都是由 yield 关键字指定的值。将 yield 视为 return 的生成器-迭代器版本,表示算法每次迭代之间的边界。每次调用 next() 时,生成器代码都会从 yield 之后的语句恢复。

当(a)它是提问者所说的“非常糟糕的文档”的副本并且(b)它说没有任何帮助时,这是如何获得约 80 票的?下面的答案要好得多。
2021-03-23 11:16:24
@NicolasBarbulesco 如果您单击进入 MDN 文档,则有一个非常明显的示例。
2021-03-24 11:16:24
在这里引用 MDN 有什么意义?我想每个人都可以在 MDN 上阅读。访问davidwalsh.name/promises以了解有关它们的更多信息。
2021-03-27 11:16:24
MDN docs是JS上最难懂的,当你只想知道它“做什么”的时候使用了很多技术术语,仅此而已。
2021-04-06 11:16:24
如果有人要求解释,只是复制粘贴文档是完全没有用的。询问意味着您已经在文档中进行了搜索,但您不了解它们。
2021-04-13 11:16:24

简化/详细阐述 Nick Sotiros 的回答(我认为这很棒),我认为最好描述一个人如何开始使用yield.

在我看来,使用的最大好处yield是它会消除我们在代码中看到的所有嵌套回调问题。一开始很难理解,这就是我决定写这个答案的原因(为我自己,也希望其他人!)

它的做法是引入协程的概念,协程是一个可以自愿停止/暂停的函数,直到它得到它需要的东西。在 javascript 中,这表示为function*只有function*函数可以使用yield.

这是一些典型的javascript:

loadFromDB('query', function (err, result) {
  // Do something with the result or handle the error
})

这很笨拙,因为现在您的所有代码(显然需要等待此loadFromDB调用)都需要在这个丑陋的回调中。这很糟糕,有几个原因......

  • 您的所有代码都缩进一级
  • 你有这个目的}),你需要到处跟踪
  • 所有这些额外的function (err, result)术语
  • 不太清楚你这样做是为了给 result

另一方面,有了yield,所有这些都可以在漂亮的协同程序框架的帮助下在一行中完成

function* main() {
  var result = yield loadFromDB('query')
}

因此,现在您的主函数将在需要等待变量和事物加载时在必要时产生。但是现在,为了运行它,您需要调用一个普通的(非协程函数)。一个简单的协程框架可以解决这个问题,所以你所要做的就是运行这个:

start(main())

并定义了开始(来自 Nick Sotiro 的回答)

function start(routine, data) {
    result = routine.next(data);
    if(!result.done) {
        result.value(function(err, data) {
            if(err) routine.throw(err); // continue next iteration of routine with an exception
            else start(routine, data);  // continue next iteration of routine normally
        });
    }
}

现在,您可以拥有更易读、易于删除且无需摆弄缩进、函数等的漂亮代码。

一个有趣的观察是,在这个例子中,yield实际上只是一个关键字,你可以放在一个带有回调的函数之前。

function* main() {
  console.log(yield function(cb) { cb(null, "Hello World") })
}

将打印“Hello World”。因此,您实际上可以yield通过简单地创建相同的函数签名(不带 cb)并返回来使用任何回调函数function (cb) {},如下所示:

function yieldAsyncFunc(arg1, arg2) {
  return function (cb) {
    realAsyncFunc(arg1, arg2, cb)
  }
}

希望有了这些知识,您可以编写更清晰、更易读的代码,并且易于删除

对于已经yield在任何地方使用的人,我确信这比回调更有意义,但我看不出这比回调更具可读性。
2021-03-15 11:16:24
我想你的意思是这function *是一个包含产量的函数这是一个称为生成器的特殊函数。
2021-03-29 11:16:24
那篇文章很难理解
2021-03-31 11:16:24
afunction*只是一个没有产量的常规函数​​?
2021-04-03 11:16:24

给出一个完整的答案:yield工作类似于return,但在生成器中。

对于通常给出的示例,其工作原理如下:

function *squareGen(x) {
    var i;
    for (i = 0; i < x; i++) {
        yield i*i;
    }
}

var gen = squareGen(3);

console.log(gen.next().value); // prints 0
console.log(gen.next().value); // prints 1
console.log(gen.next().value); // prints 4

但是 yield 关键字还有第二个目的。它可用于将值发送到生成器。

为了澄清,一个小例子:

function *sendStuff() {
    y = yield (0);
    yield y*y;
}

var gen = sendStuff();

console.log(gen.next().value); // prints 0
console.log(gen.next(2).value); // prints 4

这是有效的,因为值2被分配给y,通过将它发送到生成器,在它停止在第一个 yield (返回0)之后。

这使我们能够做一些非常时髦的事情。(查找协程)