nodejs+express一个很经典的问题--异步获取数据模板渲染

来源:互联网 发布:电视挂墙高度知乎 编辑:程序博客网 时间:2024/06/01 07:18

前言

很多时候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);        });    });});

原理是加入多个任务队列,然后最后才执行最后的方法。

0 0