Node.js 将异步转换为承诺
Node.js converting async into promise
我有一个中间件函数,它命中一个 api 和 returns 一个 ID。这工作正常,但在 returns 之前用数据调用 next。第一次渲染视图时没有数据。我更愿意将其包装在一个承诺中,并在可用时更新视图。实现这个的最佳方法是什么。我一直在努力用 Q 服务实现一些东西,但没有任何运气。
Server.js
var app = express();
var guid = require('./server/guid.js')(app);
app.use(guid.checkGUID);
guid.js
var express = require('express');
var Q = require('q');
var request = require('request');
var uuid = require('uuid');
module.exports = function(app){
var checkGUID = function(req, res, next) {
if(!app.locals.guid){
getGUID();
next();
}else{
next();
}
};
var getGUIDOptions = {
options....
};
var getGUID = function(req,res, next) {
request(getGUIDOptions, returnGUID);
};
function returnGUID(error, response, body) {
if (!error && response.statusCode == 200) {
app.locals.guid = body.guid;
return body.guid;
}else{
console.log(body);
}
};
return {
checkGUID: checkGUID
}
}
不需要使用Promises,只需要为请求调用中间件next()回调即可。这是我的写法,请注意,我尝试正确处理所有预期的错误情况。
var util=require('util'),
request = require('request');
module.exports = function(app) {
/**
* Assure we have an app-wide GUID
* @param {object} req
* @param {object} res
* @param {function} next - callback to next stage in request processing
*/
var assureGUID = function(req, res, next) {
if(app.locals.guid){ // we already have one
next();
return;
}
getGUID(req, res, next);
};
/**
* Retrieve a GUID for this app
* @param {object} req
* @param {object} res
* @param {function} next - callback to next stage in request processing
*/
var getGUID = function(req, res, next) {
var getGUIDOptions = {
//options....
};
request(getGUIDOptions, function(err,resp,body){
if(err){
next(err);
return;
}
if(200 !== resp.statusCode){
next(new Error('failed to retrieve GUID, status='+resp.statusCode));
return;
}
if(!('object'===typeof body && 'string'===typeof body.guid && body.guid)){
next(new Error('failed to retrieve GUID, resp: '+util.inspect(resp,{depth:null})));
return;
}
app.locals.guid=body.guid;
next();
});
};
return {
assureGUID: assureGUID
}
};
本质上,当请求到达此处理程序并且我们已经有了此应用程序的 GUID 时,我们继续(通过调用 next())。如果我们还没有 GUID,则会请求一个新的 GUID,当我们拥有它时,我们调用 next()。
如果您对使用 Promises 束手无策,我也很乐意附加该解决方案。
更新
好的,这是承诺的(原文如此)Promises 版本。我(和许多其他人)更喜欢 Petka Antonov 的 bluebird Promises 实现,所以我将在此处使用它而不是 Q
,如果需要,您应该能够毫不费力地将此代码转换为。
// Promises version
var util=require('util'),
Promise=require('bluebird'),
request = Promise.promisifyAll(require('request')); // see note 1.
var assureGUID = function(req,res,next){
if(app.locals.guid){ // we already have one
next();
return;
}
var getGUIDOptions = {
//options....
};
request.get(getGUIDOptions) // see note 2.
.catch(function(err){ // get request failed
next(new Error('GET request for GUID failed:'+err));
})
.spread(function(resp,body){ // see note 3.
// check for proper result
if(200 !== resp.statusCode){
throw new Error('failed to retrieve GUID, status='+resp.statusCode);
}
if(!('object'===typeof body && 'string'===typeof body.guid && body.guid)){
throw new Error('failed to retrieve GUID, resp: '+util.inspect(resp,{depth:null}));
}
app.locals.guid=body.guid;
next();
})
.catch(function(err){ // unexpected result
next(err);
});
};
一些注意事项:
Promise.promisifyAll()
将 自动 将 request
模块公开的所有函数转换为 Promise,如果它们遵循标准 f(err,res)
节点回调模式。
- 请参阅 bluebird 中关于 "promisification" 的注释,了解我们为什么需要使用
request.get()
而不是简单地使用 request()
。
- 如果调用不支持
f(err,res)
,您可以使用 Promise.spread()
到 "spread" 基本函数的可用参数。因此,由于 request.get()
是用三个参数调用的:err
(由 Promise 使用)、res
和 body
,我们需要 "spread" 剩余的两个参数进入新的 Promise 回调。
最后,虽然我没有测试过这段代码,但它应该可以工作。如果您有任何问题,请随时添加更多评论。
我有一个中间件函数,它命中一个 api 和 returns 一个 ID。这工作正常,但在 returns 之前用数据调用 next。第一次渲染视图时没有数据。我更愿意将其包装在一个承诺中,并在可用时更新视图。实现这个的最佳方法是什么。我一直在努力用 Q 服务实现一些东西,但没有任何运气。
Server.js
var app = express();
var guid = require('./server/guid.js')(app);
app.use(guid.checkGUID);
guid.js
var express = require('express');
var Q = require('q');
var request = require('request');
var uuid = require('uuid');
module.exports = function(app){
var checkGUID = function(req, res, next) {
if(!app.locals.guid){
getGUID();
next();
}else{
next();
}
};
var getGUIDOptions = {
options....
};
var getGUID = function(req,res, next) {
request(getGUIDOptions, returnGUID);
};
function returnGUID(error, response, body) {
if (!error && response.statusCode == 200) {
app.locals.guid = body.guid;
return body.guid;
}else{
console.log(body);
}
};
return {
checkGUID: checkGUID
}
}
不需要使用Promises,只需要为请求调用中间件next()回调即可。这是我的写法,请注意,我尝试正确处理所有预期的错误情况。
var util=require('util'),
request = require('request');
module.exports = function(app) {
/**
* Assure we have an app-wide GUID
* @param {object} req
* @param {object} res
* @param {function} next - callback to next stage in request processing
*/
var assureGUID = function(req, res, next) {
if(app.locals.guid){ // we already have one
next();
return;
}
getGUID(req, res, next);
};
/**
* Retrieve a GUID for this app
* @param {object} req
* @param {object} res
* @param {function} next - callback to next stage in request processing
*/
var getGUID = function(req, res, next) {
var getGUIDOptions = {
//options....
};
request(getGUIDOptions, function(err,resp,body){
if(err){
next(err);
return;
}
if(200 !== resp.statusCode){
next(new Error('failed to retrieve GUID, status='+resp.statusCode));
return;
}
if(!('object'===typeof body && 'string'===typeof body.guid && body.guid)){
next(new Error('failed to retrieve GUID, resp: '+util.inspect(resp,{depth:null})));
return;
}
app.locals.guid=body.guid;
next();
});
};
return {
assureGUID: assureGUID
}
};
本质上,当请求到达此处理程序并且我们已经有了此应用程序的 GUID 时,我们继续(通过调用 next())。如果我们还没有 GUID,则会请求一个新的 GUID,当我们拥有它时,我们调用 next()。
如果您对使用 Promises 束手无策,我也很乐意附加该解决方案。
更新
好的,这是承诺的(原文如此)Promises 版本。我(和许多其他人)更喜欢 Petka Antonov 的 bluebird Promises 实现,所以我将在此处使用它而不是 Q
,如果需要,您应该能够毫不费力地将此代码转换为。
// Promises version
var util=require('util'),
Promise=require('bluebird'),
request = Promise.promisifyAll(require('request')); // see note 1.
var assureGUID = function(req,res,next){
if(app.locals.guid){ // we already have one
next();
return;
}
var getGUIDOptions = {
//options....
};
request.get(getGUIDOptions) // see note 2.
.catch(function(err){ // get request failed
next(new Error('GET request for GUID failed:'+err));
})
.spread(function(resp,body){ // see note 3.
// check for proper result
if(200 !== resp.statusCode){
throw new Error('failed to retrieve GUID, status='+resp.statusCode);
}
if(!('object'===typeof body && 'string'===typeof body.guid && body.guid)){
throw new Error('failed to retrieve GUID, resp: '+util.inspect(resp,{depth:null}));
}
app.locals.guid=body.guid;
next();
})
.catch(function(err){ // unexpected result
next(err);
});
};
一些注意事项:
Promise.promisifyAll()
将 自动 将request
模块公开的所有函数转换为 Promise,如果它们遵循标准f(err,res)
节点回调模式。- 请参阅 bluebird 中关于 "promisification" 的注释,了解我们为什么需要使用
request.get()
而不是简单地使用request()
。 - 如果调用不支持
f(err,res)
,您可以使用Promise.spread()
到 "spread" 基本函数的可用参数。因此,由于request.get()
是用三个参数调用的:err
(由 Promise 使用)、res
和body
,我们需要 "spread" 剩余的两个参数进入新的 Promise 回调。
最后,虽然我没有测试过这段代码,但它应该可以工作。如果您有任何问题,请随时添加更多评论。