Generator functions in express with bluebird and co

I’m trying out some of the harmony features in node 0.12, in particular trying out the new generators feature. I’m doing this with co (v4), bluebird and express (v4), something like this:

 // ...
var fs = bluebird.promisifyAll(require('fs'));

// ...
app.post('/test', co.wrap(function* (req, res, next) {
    var contents = yield fs.readFileAsync('/etc/hosts', 'utf8');
    return res.send(contents);
}));
// ...

According to its documentation, co.wrap returns a normal function that returns a promise from the given generator function.

This is working fine so far, but what I’m not sure is if a) I’m leaking memory by not ‘waiting’ for the returned promise’s result and b) If I might lose an exception thrown in my generator function, or one of the modules used by it.

Is this a good approach? Do you see anything wrong with it?.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

The problem with your approach is that if your generator function will throw some exception, it will not be passed to next middleware. So you will lose it. You can use bluebird’s Promise.coroutine function to implement your own simple co wrapper, which will be working well in express:

// module: ../helpers/co.js
var Promise = require('bluebird');
var co      = Promise.coroutine;

module.exports = function(gen) {
    var coGen = co(gen);

    function handle_error(err, req, res, next) {
        return coGen.apply(this, arguments).catch(next);
    }

    function handle_request(req, res, next) {
        return coGen.apply(this, arguments).catch(next);
    }

    return gen.length > 3 ? handle_error : handle_request;
};

UPD: I have changed the realization a little. Now it takes into account the number or arguments passed into the generator: if > 3 then error handler will be used, otherwise – request handler. It’s important for express (look in the source code here and here)

Now you can use it in your code:

// module: your/router.js

// ...
var co = require('../helpers/co');    
var fs = bluebird.promisifyAll(require('fs'));

// ...
app.post('/test', co(function* (req, res, next) {
    var contents = yield fs.readFileAsync('/etc/hosts', 'utf8');
    return res.send(contents);
}));
// ...

UPD This is solution for express with version <= 4.x. Most likely express 5.x will support promises, so you’ll can use just bluebird’s Promis.coroutine without any fancy wrappers:

// module: your/router.js

// ...
var fs = bluebird.promisifyAll(require('fs'));
var co = bluebird.coroutine;    

// ...
app.post('/test', co(function*(req, res, next) {
    var contents = yield fs.readFileAsync('/etc/hosts', 'utf8');
    return res.send(contents);
}));
// ...

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply