Bluebird coroutine usage

6.4k views Asked by At

I am attempting to use Bluebird's coroutines as follows:

var p = require('bluebird');
//this should return a promise resolved to value 'v'
var d = p.coroutine(function*(v) { yield p.resolve(v); });
//however this prints 'undefined'
d(1).then(function(v){ console.log(v); });

What is incorrect here?

3

There are 3 answers

3
thefourtheye On BEST ANSWER

Quoting the documentation of coroutine,

Returns a function that can use yield to yield promises. Control is returned back to the generator when the yielded promise settles.

So, the function can make use of yield, but yield is not used to return value from the function. Whatever you are returning from that function with return statement will be the actual resolved value of the coroutine function.

Promise.coroutine simply makes yield statement wait for the promise to resolve and the actual yield expression will be evaluated to the resolved value.

In your case, the expression

yield p.resolve(v);

will be evaluated to 1 and since you are returning nothing from the function explicitly, by default, JavaScript returns undefined. That is why you are getting undefined as the result.


To fix this, you can actually return the yielded value, like this

var p = require('bluebird');

var d = p.coroutine(function* (v) {
    return yield p.resolve(v);
});

d(1).then(console.log);
1
Jeff Stone On
function co(gen) {
    const it = gen(), next = it.next.bind(it), raise = it.throw.bind(it);
    return () => new Promise((resolve, reject) => {
        const factory = (fn) => (data) => {
            try {
                const {value, done} = fn(data);
                if (done) {
                    resolve(value);
                } else {
                    Promise.resolve(value).then(factory(next), factory(raise));
                }
            } catch(reason) {
                reject(reason);
            }
        };
        factory(next)();
    });
}

const example = co(function*() {
    // const x = yield Promise.reject(12);
    const x = yield Promise.resolve(12);
    return x
    // throw x
});

example(); // Promise {<resolved>: 12}
0
Hiro On

Let us start with your code :)

var d = p.coroutine(function*(v) { 
    yield p.resolve(v); 
});

When you do d(1) , the bluebird's coroutine does it's magic and evaluates the promise function i.e p.resolve(v). Now, how coroutine works is by calling the promise function and then executing the actual yield i.e flow comes back to the actual generator after executing the yielded promise.

Now yield are not for returning values unlike the resolve function which could be used to get the values in the 'then' in case of promises.

So no value is returned and hence you get undefined.

So there are two things you can do :

First simply return the yield value explicitly :

var d = p.coroutine(function* (v) {
    return p.resolve(v);
})

this will return the value obtained in the 'yield' obtained by the execution of the promise , thus value can be obtained using 'then'. like

d(7).then((val) => {
    console.log(val);
});

but what if you have another , promise function to yield. i.e something like this :

var d = bluebird.coroutine(function* (val) {
    yield bluebird.resolve(val);
    console.log('i am here');
    yield(bluebird.resolve('here' + val));
    console.log('i am at the last');
});

then doing the return thing here would not execute the another yield function i.e if you return at the first yield value in the above code, then the code after the first yield will not execute.

So there is one thing i can do is to handle the 'then' of the given promise there itself like :

var d = bluebird.coroutine(function* (val) {
    yield bluebird.resolve(val).then(function(data) {
        // process data here
        console.log(data);
    });
});

d(4).then(() => {
    console.log('done execution');
});

and this you can do with any no of yielded promises. Here is a demo code :

var bluebird = require('bluebird');
bluebird.coroutine(function *temp() {
    console.log('starting');
    yield(new Promise((resolve, reject) => { 
            setTimeout(function() {
                    return resolve('first yield data');
            }, 2000);
    }).then((data)=>{console.log(data)}));

    yield(new Promise((resolve, reject) => {
            setTimeout(function() {
                    resolve('second yield data');
            }, 3000);
    }).then((data) => {console.log(data)}));
})().then(() => {
         console.log('finally done');
     });

This way you can yield as many promises in the bluebird and get the values resolved by them in their respective 'then' and process the resolved data.