我听说过 JavaScript 中的“yield”关键字,但我发现有关它的文档非常糟糕。有人可以向我解释(或推荐一个解释的网站)它的用法和用途吗?
JavaScript 中的 yield 关键字是什么?
迟到的回答,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
返回一个函数,该函数为您提供了一个迭代下一个值的方法。如果您有可能需要在迭代期间中断的潜在内存密集型过程,则很有用。
这真的很简单,这就是它的工作原理
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 之后的语句恢复。
简化/详细阐述 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
工作类似于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
)之后。
这使我们能够做一些非常时髦的事情。(查找协程)