问题
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?
回答1:
The MDN documentation is pretty good, IMO.
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.
回答2:
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:
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."
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.
回答3:
It's Really Simple, This is how it works
yield
keyword simply helps to pause and resume a function in any time asynchronously.- 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()');
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');
}
let _process = process();
let out1 = _process.next();
console.log(out1);
let out2 = _process.next();
console.log(out2);
let out3 = _process.next(90);
console.log(out3);
let _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().
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)
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.
Lets dig in to the yield keyword. Optionally you can add expression and set assign a default optional value. (Official doc syntax)
[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
Simply you can pass parameters to process() function with this mechanism, to execute different yield parts.
let val = yield 99;
_process.next(10);
now the val will be 10
Try It Now
Usages
- Lazy evaluation
- Infinite sequences
- Asynchronous control flows
References:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
- http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
- https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
回答4:
Simplifying/elaborating on Nick Sotiros' answer (which I think is awesome), I think it's best to describe how one would start coding with 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!)
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
.
Here's some typical 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...
- 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.
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:
start(main())
And start is defined (from Nick Sotiro' answer)
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.
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:
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!
回答5:
To give a complete answer: yield
is working similar to return
, but in a generator.
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.
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
).
This enables us to to some really funky stuff. (look up coroutine)
回答6:
It's used for iterator-generators. Basically, it allows you to make a (potentially infinite) sequence using procedural code. See Mozilla's documentation.
回答7:
yield
can also be used to eliminate callback hell, with a coroutine framework.
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:
Fibonacci sequence generator using the yield keyword.
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
回答9:
Dependency between async javascript calls.
Another good example of how yield can be used.
function request(url) {
axios.get(url).then((reponse) => {
it.next(response);
})
}
function* main() {
const result1 = yield request('http://some.api.com' );
const result2 = yield request('http://some.otherapi?id=' + result1.id );
console.log('Your response is: ' + result2.value);
}
var it = main();
it.next()
回答10:
Before you learn about yield you need to know about generators. Generators are created using the function*
syntax. Generator functions do not execute code but instead returns a type of iterator called a generator. When a value is given using the next
method, the generator function keeps executing until it comes across a yield keyword. Using yield
gives you back an object containing two values, one is value and the other is done (boolean). The value can be an array, object etc.
回答11:
A simple example:
const strArr = ["red", "green", "blue", "black"];
const strGen = function*() {
for(let str of strArr) {
yield str;
}
};
let gen = strGen();
for (let i = 0; i < 5; i++) {
console.log(gen.next())
}
//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:
console.log(gen.next());
//prints: {value: undefined, done: true}
来源:https://stackoverflow.com/questions/2282140/whats-the-yield-keyword-in-javascript