Search code examples
javascriptecmascript-6es6-promise

Generators + Promises explanation


I've been studying Promises and Generators but I got stuck in the script below:

function getFile(file) {
    return new Promise(function (resolve) {
        fakeAjax(file, resolve);
    });
}

function* getFiles() {
    var p1 = getFile('file1');
    var p2 = getFile('file2');
    var p3 = getFile('file3');

    output(yield p1);
    output(yield p2);
    output(yield p3);
}

function runner(gen) {
    var g = gen();
    function run(val) {
        val || undefined;
        var next = g.next(val);
        if(!next.done && !next.value !== undefined) {
            next.value
                .then(function(v) {
                    run(v);
                });
        }
    }
    run();
}

runner(getFiles);

What I'm trying to figure it out is what happens when I get to the first yield on getFiles? Why does this code work, I don't get it.

*EDIT: output is simply a console.log wrappend in a function. The fakeAjax function returns a text from an object based on the 'file' requested.


Solution

  • What I'm trying to figure it out is what happens when I get to the first yield on getFiles? Why does this code work, I don't get it.

    yield does three things:

    1. It pause the execution of the generator function
    2. It defines the value the caller of next() will receive in the value property. In this case, that's the promises you made.
    3. It optionally acts as an expression with the value passed into next(). That will be the file name you passed as a argument to next().

    yield is like a two-way conduit, both accepting values and passing values.

    In your code at the first yield it will return the object with the promise and pause, but it doesn't log anything to the console at this point — yield can pause mid-expression. When you call next() again it will finish the console.log and then move to the next yield. It can be a little confusing because there is usually one more call to next that there are yields. For example in this code `next is called four times and that's why you get the last console.log.

    Here's an MCVE that I assume approximates the undefined functions in your example:

    function getFile(file) {
        return new Promise(resolve => setTimeout(() => resolve(file), 1000))
    }
    
    function* getFiles() {
        var p1 = getFile('file1');
        var p2 = getFile('file2');
        var p3 = getFile('file3');
    
        console.log(yield p1); // return promise, then pause, then log value passed to next()
        console.log(yield p2);
        console.log(yield p3);
    }
    
    function runner(gen) {
        var g = gen();
        function run(val) {
            var next = g.next(val);     
            if(!next.done && !next.value !== undefined) {
                next.value
                    .then(function(v) {
                        run(v);
                    });
            }     
        }
        run();
    }
    
    runner(getFiles);