I heard about a "yield" keyword in JavaScript, but I found very poor documentation about it. Can someone explain me (or recommend a site that explains) its usage and what it is used for?
我听说过JavaScript中的“yield”关键字,但我发现它的文档非常糟糕。有人能给我解释一下(或者推荐一个解释一下)它的用法和用途吗?
8 个解决方案
#1
80
The MDN documentation is pretty good, IMO.
在我看来,MDN文档相当不错。
The function containing the yield keyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator's next() method performs another pass through the iterative algorithm. Each step's value is the value specified by the yield keyword. Think of yield as the generator-iterator version of return, indicating the boundary between each iteration of the algorithm. Each time you call next(), the generator code resumes from the statement following the yield.
包含yield关键字的函数是生成器。当您调用它时,它的形式参数绑定到实际的参数,但是它的主体实际上并没有被计算。相反,返回生成器-迭代器。对生成器-迭代器的next()方法的每个调用都执行迭代算法的另一个传递。每一步的值都是由yield关键字指定的值。把yield看作是return的生成器-迭代器版本,表示算法每次迭代之间的边界。每次调用next()时,在yield之后的语句中恢复生成器代码。
#2
134
Late answering, probably everybody knows about yield
now, but some better documentation has come along.
回答晚了,现在大家可能都知道收益率了,但是一些更好的文档已经出现了。
Adapting an example from "Javascript's Future: Generators" by James Long for the official Harmony standard:
改编自James Long的《Javascript的未来:生成器》(Javascript’s Future: generator),适用于官方的和谐标准:
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
"When you call foo, you get back a Generator object which has a next method."
“当你调用foo时,你会得到一个生成器对象,它有一个下一个方法。”
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
So yield
is kind of like return
: you get something back. return x
returns the value of x
, but yield x
returns a function, which gives you a method to iterate toward the next value. Useful if you have a potentially memory intensive procedure that you might want to interrupt during the iteration.
收益率有点像回报,你得到了一些东西。return x返回x的值,但是yield x返回一个函数,它为您提供了一个迭代到下一个值的方法。如果您有一个潜在的内存密集型过程,您可能希望在迭代期间中断该过程,那么这将非常有用。
#3
43
Simplifying/elaborating on Nick Sotiros' answer (which I think is awesome), I think it's best to describe how one would start coding with with yield
.
简化/详细阐述Nick Sotiros的答案(我认为这个答案非常棒),我认为最好描述一下如何开始使用yield进行编码。
In my opinion, the biggest advantage of using yield
is that it will eliminate all the nested callback problems we see in code. It's hard to see how at first, which is why I decided to write this answer (for myself, and hopefully others!)
在我看来,使用yield的最大好处是它将消除我们在代码中看到的所有嵌套回调问题。很难想象一开始是怎么写的,这就是为什么我决定写这个答案(对我自己来说,希望对其他人也是如此!)
The way it does it is by introducing the idea of a co-routine, which is a function that can voluntarily stop/pause until it gets what it needs. In javascript, this is denoted by function*
. Only function*
functions can use yield
.
它的方法是引入共同例行程序的概念,这是一个可以自动停止/暂停直到它得到它需要的功能。在javascript中,这用函数*表示。只有函数*函数可以使用yield。
Here's some typical javascript:
这里有一些典型的javascript:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
This is clunky because now all of your code (which obviously needs to wait for this loadFromDB
call) needs to be inside this ugly looking callback. This is bad for a few reasons...
这很麻烦,因为现在所有的代码(显然需要等待loadFromDB调用)都需要放在这个难看的回调中。这很不好,有几个原因……
- All of your code is indented one level in
- 所有代码都缩进了一个级别
- You have this end
})
which you need to keep track of everywhere - 你有这样的目的,你需要追踪到任何地方。
- All this extra
function (err, result)
jargon - 所有这些额外的函数(呃,结果)术语
- Not exactly clear that you're doing this to assign a value to
result
- 不清楚您这样做是为了给结果赋值
On the other hand, with yield
, all of this can be done in one line with the help of the nice co-routine framework.
另一方面,在yield的帮助下,所有这些都可以在一行中完成。
function* main() {
var result = yield loadFromDB('query')
}
And so now your main function will yield where necessary when it needs to wait for variables and things to load. But now, in order to run this, you need to call a normal (non-coroutine function). A simple co-routine framework can fix this problem so that all you have to do is run this:
现在你的主函数会在需要等待变量和东西加载时产生。但是现在,要运行这个函数,需要调用一个普通的(非coroutine函数)。一个简单的联合例程框架可以解决这个问题,所以你所要做的就是运行这个:
start(main())
And start is defined (from Nick Sotiro' answer)
start是定义的(从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
});
}
}
And now, you can have beautiful code that is much more readable, easy to delete, and no need to fiddle with indents, functions, etc.
现在,您可以有更漂亮的代码,可读性更好,易于删除,不需要修改缩进、函数等等。
An interesting observation is that in this example, yield
is actually just a keyword you can put before a function with a callback.
一个有趣的观察是,在这个示例中,yield实际上只是一个关键字,可以放在具有回调的函数之前。
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
Would print "Hello World". So you can actually turn any callback function into using yield
by simply creating the same function signature (without the cb) and returning function (cb) {}
, like so:
将打印“Hello World”。因此,通过创建相同的函数签名(不包含cb)并返回函数(cb){},可以将任何回调函数转换为yield,如下所示:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Hopefully with this knowledge you can write cleaner, more readable code that is easy to delete!
希望有了这些知识,您可以编写更清晰、更易读、更易于删除的代码!
#4
26
It's Really Simple, This is how it works
很简单,就是这样
-
yield
keyword simply helps to pause and resume a function in any time asynchronously. - yield关键字可以帮助在任何时候异步地暂停和恢复函数。
- Additionally it helps to return value from a generator function.
- 此外,它还有助于从生成器函数返回值。
Take this simple generator function:
以这个简单的生成器函数为例:
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()');
yield;
console.log('Resumed process3');
console.log('End of the process function');
}
let _process = process();
让_process =过程();
Until you call the _process.next() it wont execute the first 2 lines of code, then the first yield will pause the function. To resume the function until next pause point (yield keyword) you need to call _process.next().
在调用_process.next()之前,它不会执行前两行代码,然后第一个yield将暂停函数。要将函数恢复到下一个暂停点(yield关键字),需要调用_process.next()。
You can think multiple yields are the breakpoints in a javascript debugger within a single function. Until you tell to navigate next breakpoint it wont execute the code block. (Note: without blocking the whole application)
您可以认为多个收益是一个函数中的javascript调试器中的断点。在您告诉导航下一个断点之前,它不会执行代码块。(注意:不阻塞整个应用程序)
But while yield performs this pause and resume behaviours it can return some results as well {value: any, done: boolean}
according to the previous function we haven't emit any values. If we explore the previous output it will show the same { value: undefined, done: false }
with value undefined.
但是,尽管yield执行了暂停和恢复行为,它也会返回一些结果,如{value: any, done: boolean}根据前面的函数,我们不会发出任何值。如果我们研究前面的输出,它将显示相同的{value: undefined, done: false},值为undefined。
Lets dig in to the yield keyword. Optionally you can add expression and set assign a default optional value. (Official doc syntax)
让我们深入研究一下yield关键字。您还可以添加表达式和设置一个默认的可选值。(官方文档语法)
[rv] = yield [expression];
expression: Value to return from the generator function
表达式:从生成器函数返回的值
yield any;
yield {age: 12};
rv: Returns the optional value that passed to the generator's next() method
rv:返回传递给生成器的next()方法的可选值。
let val = yield 99;
_process.next(10);
now the val will be 10
现在试一试
Usages
用法
- Lazy evaluation
- 懒惰的评价
- Infinite sequences
- 无限的序列
- Asynchronous control flows
- 异步控制流
References:
引用:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
- http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
- http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
- https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
- https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
#5
16
It's used for iterator-generators. Basically, it allows you to make a (potentially infinite) sequence using procedural code. See Mozilla's documentation.
它是用于iterator-generators。基本上,它允许您使用过程代码创建(可能是无限的)序列。看到Mozilla的文档。
#6
15
To give a complete answer: yield
is working similar to return
, but in a generator.
给出一个完整的答案:yield与return类似,但在生成器中。
As for the commonly given example, this works as follows:
对于一般的例子,如下所示:
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
But theres also a second purpose of the yield keyword. It can be used to send values to the generator.
但是,yield关键字还有第二个用途。它可以用来向生成器发送值。
To clarify, a small example:
为了澄清这一点,举个小例子:
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
This works, as the value 2
is assigned to y
, by sending it to the generator, after it stopped at the first yield (which returned 0
).
当值2被赋值给y时,这个值就会被发送到生成器中,在第一个yield(返回0)停止之后。
This enables us to to some really funky stuff. (look up coroutine)
这能让我们得到一些非常有趣的东西。(查找协同程序)
#7
5
yield
can also be used to eliminate callback hell, with a coroutine framework.
yield还可以用于消除回调地狱,使用coroutine框架。
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
});
}
}
// with nodejs as 'node --harmony'
fs = require('fs');
function read(path) {
return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); };
}
function* routine() {
text = yield read('/path/to/some/file.txt');
console.log(text);
}
// with mdn javascript 1.7
http.get = function(url) {
return function(callback) {
// make xhr request object,
// use callback(null, resonseText) on status 200,
// or callback(responseText) on status 500
};
};
function* routine() {
text = yield http.get('/path/to/some/file.txt');
console.log(text);
}
// invoked as.., on both mdn and nodejs
start(routine());
#8
3
Fibonacci sequence generator using the yield keyword.
使用yield关键字的Fibonacci序列生成器。
function* fibbonaci(){
var a = -1, b = 1, c;
while(1){
c = a + b;
a = b;
b = c;
yield c;
}
}
var fibonacciGenerator = fibbonaci();
fibonacciGenerator.next().value; // 0
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2
#1
80
The MDN documentation is pretty good, IMO.
在我看来,MDN文档相当不错。
The function containing the yield keyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator's next() method performs another pass through the iterative algorithm. Each step's value is the value specified by the yield keyword. Think of yield as the generator-iterator version of return, indicating the boundary between each iteration of the algorithm. Each time you call next(), the generator code resumes from the statement following the yield.
包含yield关键字的函数是生成器。当您调用它时,它的形式参数绑定到实际的参数,但是它的主体实际上并没有被计算。相反,返回生成器-迭代器。对生成器-迭代器的next()方法的每个调用都执行迭代算法的另一个传递。每一步的值都是由yield关键字指定的值。把yield看作是return的生成器-迭代器版本,表示算法每次迭代之间的边界。每次调用next()时,在yield之后的语句中恢复生成器代码。
#2
134
Late answering, probably everybody knows about yield
now, but some better documentation has come along.
回答晚了,现在大家可能都知道收益率了,但是一些更好的文档已经出现了。
Adapting an example from "Javascript's Future: Generators" by James Long for the official Harmony standard:
改编自James Long的《Javascript的未来:生成器》(Javascript’s Future: generator),适用于官方的和谐标准:
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
"When you call foo, you get back a Generator object which has a next method."
“当你调用foo时,你会得到一个生成器对象,它有一个下一个方法。”
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
So yield
is kind of like return
: you get something back. return x
returns the value of x
, but yield x
returns a function, which gives you a method to iterate toward the next value. Useful if you have a potentially memory intensive procedure that you might want to interrupt during the iteration.
收益率有点像回报,你得到了一些东西。return x返回x的值,但是yield x返回一个函数,它为您提供了一个迭代到下一个值的方法。如果您有一个潜在的内存密集型过程,您可能希望在迭代期间中断该过程,那么这将非常有用。
#3
43
Simplifying/elaborating on Nick Sotiros' answer (which I think is awesome), I think it's best to describe how one would start coding with with yield
.
简化/详细阐述Nick Sotiros的答案(我认为这个答案非常棒),我认为最好描述一下如何开始使用yield进行编码。
In my opinion, the biggest advantage of using yield
is that it will eliminate all the nested callback problems we see in code. It's hard to see how at first, which is why I decided to write this answer (for myself, and hopefully others!)
在我看来,使用yield的最大好处是它将消除我们在代码中看到的所有嵌套回调问题。很难想象一开始是怎么写的,这就是为什么我决定写这个答案(对我自己来说,希望对其他人也是如此!)
The way it does it is by introducing the idea of a co-routine, which is a function that can voluntarily stop/pause until it gets what it needs. In javascript, this is denoted by function*
. Only function*
functions can use yield
.
它的方法是引入共同例行程序的概念,这是一个可以自动停止/暂停直到它得到它需要的功能。在javascript中,这用函数*表示。只有函数*函数可以使用yield。
Here's some typical javascript:
这里有一些典型的javascript:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
This is clunky because now all of your code (which obviously needs to wait for this loadFromDB
call) needs to be inside this ugly looking callback. This is bad for a few reasons...
这很麻烦,因为现在所有的代码(显然需要等待loadFromDB调用)都需要放在这个难看的回调中。这很不好,有几个原因……
- All of your code is indented one level in
- 所有代码都缩进了一个级别
- You have this end
})
which you need to keep track of everywhere - 你有这样的目的,你需要追踪到任何地方。
- All this extra
function (err, result)
jargon - 所有这些额外的函数(呃,结果)术语
- Not exactly clear that you're doing this to assign a value to
result
- 不清楚您这样做是为了给结果赋值
On the other hand, with yield
, all of this can be done in one line with the help of the nice co-routine framework.
另一方面,在yield的帮助下,所有这些都可以在一行中完成。
function* main() {
var result = yield loadFromDB('query')
}
And so now your main function will yield where necessary when it needs to wait for variables and things to load. But now, in order to run this, you need to call a normal (non-coroutine function). A simple co-routine framework can fix this problem so that all you have to do is run this:
现在你的主函数会在需要等待变量和东西加载时产生。但是现在,要运行这个函数,需要调用一个普通的(非coroutine函数)。一个简单的联合例程框架可以解决这个问题,所以你所要做的就是运行这个:
start(main())
And start is defined (from Nick Sotiro' answer)
start是定义的(从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
});
}
}
And now, you can have beautiful code that is much more readable, easy to delete, and no need to fiddle with indents, functions, etc.
现在,您可以有更漂亮的代码,可读性更好,易于删除,不需要修改缩进、函数等等。
An interesting observation is that in this example, yield
is actually just a keyword you can put before a function with a callback.
一个有趣的观察是,在这个示例中,yield实际上只是一个关键字,可以放在具有回调的函数之前。
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
Would print "Hello World". So you can actually turn any callback function into using yield
by simply creating the same function signature (without the cb) and returning function (cb) {}
, like so:
将打印“Hello World”。因此,通过创建相同的函数签名(不包含cb)并返回函数(cb){},可以将任何回调函数转换为yield,如下所示:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Hopefully with this knowledge you can write cleaner, more readable code that is easy to delete!
希望有了这些知识,您可以编写更清晰、更易读、更易于删除的代码!
#4
26
It's Really Simple, This is how it works
很简单,就是这样
-
yield
keyword simply helps to pause and resume a function in any time asynchronously. - yield关键字可以帮助在任何时候异步地暂停和恢复函数。
- Additionally it helps to return value from a generator function.
- 此外,它还有助于从生成器函数返回值。
Take this simple generator function:
以这个简单的生成器函数为例:
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()');
yield;
console.log('Resumed process3');
console.log('End of the process function');
}
let _process = process();
让_process =过程();
Until you call the _process.next() it wont execute the first 2 lines of code, then the first yield will pause the function. To resume the function until next pause point (yield keyword) you need to call _process.next().
在调用_process.next()之前,它不会执行前两行代码,然后第一个yield将暂停函数。要将函数恢复到下一个暂停点(yield关键字),需要调用_process.next()。
You can think multiple yields are the breakpoints in a javascript debugger within a single function. Until you tell to navigate next breakpoint it wont execute the code block. (Note: without blocking the whole application)
您可以认为多个收益是一个函数中的javascript调试器中的断点。在您告诉导航下一个断点之前,它不会执行代码块。(注意:不阻塞整个应用程序)
But while yield performs this pause and resume behaviours it can return some results as well {value: any, done: boolean}
according to the previous function we haven't emit any values. If we explore the previous output it will show the same { value: undefined, done: false }
with value undefined.
但是,尽管yield执行了暂停和恢复行为,它也会返回一些结果,如{value: any, done: boolean}根据前面的函数,我们不会发出任何值。如果我们研究前面的输出,它将显示相同的{value: undefined, done: false},值为undefined。
Lets dig in to the yield keyword. Optionally you can add expression and set assign a default optional value. (Official doc syntax)
让我们深入研究一下yield关键字。您还可以添加表达式和设置一个默认的可选值。(官方文档语法)
[rv] = yield [expression];
expression: Value to return from the generator function
表达式:从生成器函数返回的值
yield any;
yield {age: 12};
rv: Returns the optional value that passed to the generator's next() method
rv:返回传递给生成器的next()方法的可选值。
let val = yield 99;
_process.next(10);
now the val will be 10
现在试一试
Usages
用法
- Lazy evaluation
- 懒惰的评价
- Infinite sequences
- 无限的序列
- Asynchronous control flows
- 异步控制流
References:
引用:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
- http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
- http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
- https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
- https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
#5
16
It's used for iterator-generators. Basically, it allows you to make a (potentially infinite) sequence using procedural code. See Mozilla's documentation.
它是用于iterator-generators。基本上,它允许您使用过程代码创建(可能是无限的)序列。看到Mozilla的文档。
#6
15
To give a complete answer: yield
is working similar to return
, but in a generator.
给出一个完整的答案:yield与return类似,但在生成器中。
As for the commonly given example, this works as follows:
对于一般的例子,如下所示:
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
But theres also a second purpose of the yield keyword. It can be used to send values to the generator.
但是,yield关键字还有第二个用途。它可以用来向生成器发送值。
To clarify, a small example:
为了澄清这一点,举个小例子:
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
This works, as the value 2
is assigned to y
, by sending it to the generator, after it stopped at the first yield (which returned 0
).
当值2被赋值给y时,这个值就会被发送到生成器中,在第一个yield(返回0)停止之后。
This enables us to to some really funky stuff. (look up coroutine)
这能让我们得到一些非常有趣的东西。(查找协同程序)
#7
5
yield
can also be used to eliminate callback hell, with a coroutine framework.
yield还可以用于消除回调地狱,使用coroutine框架。
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
});
}
}
// with nodejs as 'node --harmony'
fs = require('fs');
function read(path) {
return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); };
}
function* routine() {
text = yield read('/path/to/some/file.txt');
console.log(text);
}
// with mdn javascript 1.7
http.get = function(url) {
return function(callback) {
// make xhr request object,
// use callback(null, resonseText) on status 200,
// or callback(responseText) on status 500
};
};
function* routine() {
text = yield http.get('/path/to/some/file.txt');
console.log(text);
}
// invoked as.., on both mdn and nodejs
start(routine());
#8
3
Fibonacci sequence generator using the yield keyword.
使用yield关键字的Fibonacci序列生成器。
function* fibbonaci(){
var a = -1, b = 1, c;
while(1){
c = a + b;
a = b;
b = c;
yield c;
}
}
var fibonacciGenerator = fibbonaci();
fibonacciGenerator.next().value; // 0
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2