260

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?

user2428118
  • 7,499
  • 4
  • 41
  • 69
mck89
  • 17,869
  • 15
  • 83
  • 101
  • He probably means 'Yield' http://bytes.com/topic/python/answers/685510-yield-keyword-usage – ant Feb 17 '10 at 15:58
  • 5
    it's explained in [MDN](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators), but I think this only works for firefox, right? How portable is it? Any way to to this on Chrome or node.js? PD: sorry, it's **Javascript v1.7+**, so that's the property to look at when looking for support. – Trylks Oct 12 '12 at 15:48
  • 2
    @Trylks: Generators are available in Node since v0.11.2 – Janus Troelsen Feb 26 '14 at 13:23
  • @JanusTroelsen however, only behind a flag. They are supported natively in ioJS – Dan Jul 12 '15 at 14:38
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield – chharvey Feb 23 '18 at 06:21
  • **Beware:** `yield` is not supported by Internet Explorer – Krisztián Balla Mar 04 '19 at 08:20

14 Answers14

233

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.

Community
  • 1
  • 1
bishop
  • 32,403
  • 9
  • 89
  • 122
  • 14
    Helpful, but i guess you its `function* foo(x){` there – Rana Deep Jan 05 '14 at 13:13
  • 9
    @RanaDeep: [The function syntax is extended to add an *optional* `*` token](http://wiki.ecmascript.org/doku.php?id=harmony:generators). Whether or not you need it depends upon the kind of future you are returning. The detail is long: [GvR explains it for the Python implementation](https://groups.google.com/forum/#!msg/python-tulip/bmphRrryuFk/aB45sEJUomYJ), upon which the Javascript implementation is modeled. Using `function *` will always be right, though in some cases slightly more overhead than `function` with `yield`. – bishop Jan 06 '14 at 13:56
  • @bishop I think you're interpreting that wrong. The page does say "The function syntax is extended to add an _optional_ * token", but then goes on to say "A function with a * token is known as a generator function." and "An early error is raised if a yield or yield* expression occurs in a non-generator function." Plus, Babel [considers the above code to be a syntax error](https://goo.gl/ZHKgkt). The code above is broken, and won't work without a `*` added after the `function` keyword. – Ajedi32 Dec 08 '15 at 15:25
  • 1
    @Ajedi32 Yep, you're right. Harmony standardized the correlation between `function *` and `yield`, and added the quoted error ("An early error is raised if a yield or yield* expression occurs in a non-generator function"). But, the original Javascript 1.7 implementation in Firefox [didn't require the `*`](https://web.archive.org/web/20130823145610/https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.7). Updated answer accordingly. Thanks! – bishop Dec 08 '15 at 15:48
  • just for info , this kind of concept is also there in C# – refactor Apr 06 '16 at 13:22
  • 3
    @MuhammadUmer Js is finally becomes a language you can actualy use. It's called evolution. – Lukas Liesis Nov 20 '16 at 21:38
  • 1
    example is useful,but...what's a function * ? – Don Diego Feb 07 '18 at 11:42
85

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.

Matt Ball
  • 332,322
  • 92
  • 617
  • 683
  • 2
    @NicolasBarbulesco there is a very obviously-placed example if you click through to the MDN documentation. – Matt Ball May 08 '14 at 14:32
  • @MattBall - would a function as javascript for PI like this be sufficient as follows: function * PI { PI = (( Math.SQRT8; ) / 9801;); } - or is there already a function implemented in javascript for this calculation of PI ? – dschinn1001 Dec 29 '15 at 22:06
  • @dschinn1001 this comment is entirely unrelated to the question here. That said: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI – Matt Ball Dec 29 '15 at 22:22
  • 4
    What's the point of quoting MDN here? I think everyone can read that on MDN. Visit https://davidwalsh.name/promises to learn more about them. – Ejaz Karim Jul 22 '17 at 11:02
  • 42
    How did this get ~80 upvotes when (a) it is a copy of the "very poor documentation" as the questioner calls it and (b) it says nothing helpful? Far better answers below. – www-0av-Com Oct 14 '17 at 16:39
  • 9
    if someone asks for explanation, just copy pasting a documentation is totally unuseful. Asking means that you already searched in docs but you did not understand them. – Don Diego Feb 07 '18 at 11:37
  • its just a function that saves its state. – john ktejik Apr 03 '19 at 02:56
  • 3
    MDN docs are the most incomprehensible on JS, using a lot of technical words when you just want to know "what" it does, that's it. – dawn Oct 06 '20 at 17:24
  • I came here after reading the same documentation for more detail/example but look at the answer. – CodeGeek Apr 19 '21 at 16:14
80

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

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:

noelyahan
  • 3,769
  • 29
  • 25
57

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!

jamietelin
  • 16,631
  • 14
  • 52
  • 69
Leander
  • 643
  • 5
  • 8
19

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)

David
  • 614
  • 4
  • 12
16

It's used for iterator-generators. Basically, it allows you to make a (potentially infinite) sequence using procedural code. See Mozilla's documentation.

Bill
  • 13,703
  • 14
  • 67
  • 112
Matthew Flaschen
  • 255,933
  • 45
  • 489
  • 528
7

Yeild keyword in javaScript function makes it generator,

what is generator in javaScript?

A generator is a function that produces a sequence of results instead of a single value, i.e you generate ​a series of values

Meaning generators helps us work asynchronously with the help iterators, Oh now what the hack iterators are? really?

Iterators are mean through which we are able to access items one at a time

from where iterator help us accessing item one at a time? it help us accessing items through generator functions,

generator functions are those in which we use yeild keyword, yield keyword help us in pausing and resuming execution of function

here is quick example

function *getMeDrink() {

    let question1 = yield 'soda or beer' // execution will pause here because of yield

 if (question1 == 'soda') {

            return 'here you get your soda'

    }

    if (question1 == 'beer') {

        let question2 = yield 'Whats your age' // execution will pause here because of yield

        if (question2 > 18) {

            return "ok you are eligible for it"

        } else {

            return 'Shhhh!!!!'

        }
    }
}


let _getMeDrink = getMeDrink() // initialize it

_getMeDrink.next().value  // "soda or beer"

_getMeDrink.next('beer').value  // "Whats your age"

_getMeDrink.next('20').value  // "ok you are eligible for it"

_getMeDrink.next().value // undefined

let me brifly explain what is going on

you noticed execution is being paused at each yeild keyword and we are able to access first yield with help of iterator .next()

this iterates to all yield keywords one at a time and then returns undefined when there is no more yield keywords left in simple words you can say yield keyword is break point where function each time pauses and only resume when call it using iterator

for our case: _getMeDrink.next() this is example of iterator that is helping us accessing each break point in function

Example of Generators: async/await

if you see implementation of async/await you will see generator functions & promises are used to make async/await work

please point out any suggestions is welcomed

Hanzla Habib
  • 1,707
  • 15
  • 18
6

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());
Nick Sotiros
  • 1,802
  • 1
  • 19
  • 16
4

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 
nikksan
  • 2,699
  • 2
  • 16
  • 25
3

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()
  • Beautiful example and exactly what I was looking for to understand. Without the request() function it was not clear to me who is calling the it.next() with a value. – SijuMathew Dec 17 '20 at 14:30
0

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.

0

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}
0

I am also trying to understand the yield keyword. Based my current understanding, in generator, yield keyword works like a CPU context-switch. When yield statement is run, all states (for example, local variables) are saved.

Besides this, a direct result object will be returned to the caller, like { value: 0, done: false }. The caller can use this result object to decide whether to 'wake up' the generator again by calling next() (calling next() is to iterate the execution).

Another important thing is that it can set a value to a local variable. This value can be passed by the 'next()' caller when 'waking up' the generator. for example, it.next('valueToPass'), like this: "resultValue = yield slowQuery(1);" Just like when waking up a next execution, caller can inject some running result to the execution (injecting it to local variable). Thus, for this execution, there are two kind of state:

  1. the context that saved in the last execution.

  2. The injected values by this execution's trigger.

So, with this feature, the generator can sort out multiple async operations. The result of first async query will be passed to the second one by setting local variable (resultValue in above example). The second async query can only be triggered by the first's async query's response. Then the second async query can check the local variable value to decide next steps because the local variable is an injected value from first query’s response.

The difficulties of async queries are:

  1. callback hell

  2. lose of context unless passing them as parameters in the callback.

yield and generator can help on both.

Without yield and generator, to sort out multiple async query requires nested callback with parameters as context which does not easy to read and maintain.

Below is a chained async queries example which running with nodejs:

const axios = require('axios');

function slowQuery(url) {        
    axios.get(url)
    .then(function (response) {
            it.next(1);
    })
    .catch(function (error) {
            it.next(0);
    })
}

function* myGen(i=0) {
    let queryResult = 0;

    console.log("query1", queryResult);
    queryResult = yield slowQuery('https://google.com');


    if(queryResult == 1) {
        console.log("query2", queryResult);
        //change it to the correct url and run again.
        queryResult = yield slowQuery('https://1111111111google.com');
    }

    if(queryResult == 1) {
        console.log("query3", queryResult);
        queryResult =  yield slowQuery('https://google.com');
    } else {
        console.log("query4", queryResult);
        queryResult = yield slowQuery('https://google.com');
    }
}

console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");

Below is the running result:

+++++++++++start+++++++++++

query1 0

+++++++++++end+++++++++++

query2 1

query4 0

Below state pattern can do the similar thing for above example:

const axios = require('axios');

function slowQuery(url) {
    axios.get(url)
        .then(function (response) {
            sm.next(1);
        })
        .catch(function (error) {
            sm.next(0);
        })
}

class StateMachine {
        constructor () {
            this.handler = handlerA;
            this.next = (result = 1) => this.handler(this, result);
        }
}

const handlerA = (sm, result) => {
                                    const queryResult = result; //similar with generator injection
                                    console.log("query1", queryResult);
                                    slowQuery('https://google.com');
                                    sm.handler = handlerB; //similar with yield;
                                };

const handlerB = (sm, result) => {
                                    const queryResult = result; //similar with generator injection
                                    if(queryResult == 1) {
                                        console.log("query2", queryResult);
                                        slowQuery('https://1111111111google.com');
                                    }
                                    sm.handler = handlerC; //similar with yield;
                                };

const handlerC = (sm, result) => {
                                    const queryResult = result; //similar with generator injection;
                                    if (result == 1 ) {
                                        console.log("query3", queryResult);
                                        slowQuery('https://google.com');
                                    } else {
                                        console.log("query4", queryResult);
                                        slowQuery('https://google.com');
                                    }
                                    sm.handler = handlerEnd; //similar with yield;
                                };

const handlerEnd = (sm, result) => {};

console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");

Following is the running result:

+++++++++++start+++++++++++

query1 0

+++++++++++end+++++++++++

query2 1

query4 0

pete
  • 1
  • 1
0

don't forget the very helpful 'x of generator' syntax to loop through the generator. No need to use the next() function at all.

function* square(x){
    for(i=0;i<100;i++){
        x = x * 2;
        yield x;        
    }   
}

var gen = square(2);
for(x of gen){
   console.log(x);
}
john ktejik
  • 4,724
  • 3
  • 38
  • 48