很多时候nodejs都会有一些异步操作,譬如,读写文件,譬如,用httprequest来获取后台数据然后渲染,但是由于nodejs的特性,很多时候都是异步的,那么如何解决。
/**
* 商品分类相关api。
*
*/
var request = require('request');
var Settings=require('../settings.js');
var fs = require('fs');
var sysUtil = require('../util/SystemUtil.js');
var logger=sysUtil.Logger;
exports.getMenuList=function(callback){
logger.info("访问url:"+Settings.WebApi.ApiHost+Settings.WebApi.IndexMenuApi);
var _url=Settings.WebApi.ApiHost+Settings.WebApi.IndexMenuApi;
//_url="http://www.baidu.com";
request(_url, function (error, response, body) {
if (!error && response.statusCode == 200) {
if(callback){
callback(response,body);
}
}
else{
console.log("后台通信端错误。");
}
}).pipe(fs.createWriteStream('./test/file1.txt'));
};
var express = require('express');
var router = express.Router();
var Settings=require("../settings");
var sysUtil=require("../util/SystemUtil.js");
var logger=sysUtil.Logger;
var fs=require("fs");
/* GET home page. */
router.get('/', function(req, res, next) {
//console.log("================");
//console.log(Settings);
//--获取首页菜单数据。
var CategoryService=require("../service/CategoryService.js");
var _menuList={};
CategoryService.getMenuList(function(res1,body1){
_menuList=JSON.parse(body1);
//fs.writeFile("./test/file1.txt",JSON.stringify(_menuList),function(){});
});
res.render('index', { title: 'Express' ,menuList:_menuList});
});
module.exports = router;
ok,我们会发现,渲染模板的时候,menuList永远为空。。。。
一个比较可行的方法是用deffered 、promise方案,当然,request这个也需要用deffered promise封装一下,否则会写死人的。
下面是项目中应用的成熟方案代码,部分:
/**
* 该文件是针对request获取及存储数据的封装,当然,包含了promise的帮助。
*/
var logger = require('../util/logHelper.js').Logger;
var fs=require("fs");
var Q = require('q');
var util=require("../util/util.js");
var request = require('request');
var Extend=require("util")._extend;
var RequestHelper=function(){
this.taskList=[];
};
RequestHelper.prototype.addTask=function(_opt){
this.taskList.push(_opt);
}
RequestHelper.prototype.request=function(_opts){
var _deferred= Q.defer();
var _i_settings={
url:""
,method:"get"
,data:{}
,dataType:"json"
,error:function(error){
}
,success:function(sdata){
}
};
_i_settings=Extend(_i_settings,_opts);
if(_i_settings.method.toLocaleLowerCase()=="get"){
var _realUrl=util.Json2URL(_i_settings.url,_i_settings.data);
logger.info(_realUrl);
var req2=request(_realUrl, function (error, response, body) {
if (!error && response.statusCode == 200) {
var _res=body;
try{
if(_i_settings.dataType=="json"){
_res=JSON.parse(_res);
}
}
catch (ex){
var _error_msg="nodejs can not trans this data to JSON format: "+body;
logger.error("json translate error:"+_i_settings.url);
logger.error(_error_msg);
logger.error(body);
_deferred.reject(_error_msg);
return;
}
_deferred.resolve(_res);}
else{
var _error_msg="request helper error: can not request url:"+_realUrl+"";
logger.error(_error_msg);
logger.error(body);
_deferred.reject(error);
}
});
}
else{
//--post方式。
request.post({
url:_i_settings.url
,form:_i_settings.data
},function(err,response,body){
if (!err && response.statusCode == 200) {
var _res=body;
try{
if(_i_settings.dataType.toLocaleLowerCase()=="json"){
_res=JSON.parse(_res);
}
}
catch (ex){
var _error_msg="nodejs can not trans this data to JSON format: "+body;
logger.error("json translate error:"+_i_settings.url);
logger.error(_error_msg);
logger.error(body);
_deferred.reject(_error_msg);
return;
}
_deferred.resolve(_res);}
else{
var _error_msg="request helper error: can not request url:"+_i_settings.url+"";
logger.error(_error_msg);
logger.error(err);
logger.error(body);
_deferred.reject(err);
}
});
}
var _promise= _deferred.promise;
_promise.then(_i_settings.success,_i_settings.error);
return _promise;
};
RequestHelper.prototype.run=function(onDone){
var me=this;
var taskList=this.taskList;
if(taskList.length<=0){
if(onDone){
onDone();
}
return;
}
var _now_taskReq=[];
for(var i=0;i< this.taskList.length;i++){
var _taskItem={
url:""
,success:function(parameter){
}
,error:function(parameter){
}
,method:"get"
,data:{}
,dataType:"json"
};
Extend(_taskItem,this.taskList[i]);
var _promise=me.request(_taskItem);
_now_taskReq.push(_promise);
}
//--检查是不是有多于或等于一个任务。
if(_now_taskReq.length<=0){
var _warning_msg="request helper warning:task list has less than 1 task,do not send any request now.";
logger.warn(_warning_msg);
if(onDone){
onDone();
}
return;
}
Q.all(_now_taskReq)
.spread(function(){}).done(function(){
if(onDone){
onDone();
}
});
};
module.exports=RequestHelper;
如何使用:
//商品推荐
router.get('/recommend',function(req,res,next){
GlobalService.getCommonPageData(req,function(commonData){
//res.send(JSON.stringify(commonData));
var pageData={
list:[]
,ads:[]
};
var ajaxHelper=new RequestHepler();
ajaxHelper.addTask({
url:ServerConf.ApiHost+Settings.WebApi.RecommendProductApi
,success:function(data){
var _json=data;
pageData.list=_json.rows;
}
,error:function(error){
logger.error(error);
}
});
ajaxHelper.addTask({
url:ServerConf.ApiHost+Settings.WebApi.RecommendProductAdApi
,success:function(data){
var _json=data;
pageData.ads=_json.rows;
}
,error:function(error){
logger.error(error);
}
});
ajaxHelper.run(function(){
commonData["pageData"]=pageData;
res.render('recommend', commonData);
});
});
});
原理是加入多个任务队列,然后最后才执行最后的方法。