与 bluebird 和 co 一起快速生成生成器功能

Generator functions in express with bluebird and co

我正在尝试节点 0.12 中的一些和谐功能,特别是尝试新的生成器功能。我正在使用 co (v4)、bluebird 和 express (v4) 执行此操作,如下所示:

 // ...
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);
}));
// ...

根据其文档,co.wrap returns 一个普通函数,returns 来自给定生成器函数的承诺。

到目前为止一切正常,但我不确定是否 a) 我不是 'waiting' 为返回的 promise 的结果泄漏内存,以及 b) 如果我可能丢失抛出的异常在我的生成器函数中,或者它使用的模块之一。

这是一个好方法吗?你看有什么问题吗?

你的方法的问题是如果你的生成器函数会抛出一些异常,它不会被传递给下一个中间件。所以你会失去它。您可以使用 bluebird 的 Promise.coroutine 函数来实现您自己的简单 co 包装器,它将在 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: 我对实现做了一点改动。现在它考虑了传递给生成器的数字或参数:如果 > 3 则将使用错误处理程序,否则 - 请求处理程序。对express很重要(看源码here and here

现在您可以在代码中使用它了:

// 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 这是 express 版本 <= 4.x 的解决方案。最有可能表达 5.x will support promises,因此您可以只使用 bluebird 的 Promis.coroutine 而无需任何花哨的包装器:

// 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);
}));
// ...