nodejs的nodejs 回调函数 参数里为什么需要return

nodejs回调函数和返回值 - CNode技术社区
这家伙很懒,什么个性签名都没有留下。
现在我需要用Nodejs抓取一个网站上面的数据,数据可能有几千条,分几十页,而且每一页都是一次请求,现在我需要对外部提供一个接口,就是可以一次返回这几千条数据的接口。
问题是是获取页数之后循环请求,但是返回的数据在每次请求的回调函数里,我该怎么把这些回调函数里面的数据汇总到一起并返回给调用接口啊?
用 eventproxy
使用计数器.
你要分发多少个处理函数,就要对每一个完成的回调函数进行计数判断:
function check () {
if (nums === 0) { // do something. }
每一个回调函数中都使用这个检测函数来进行逻辑监测,以回到下一个进程线
类似于交换机侦听
…(…, function (…) {
比如我写的howdo吧,虽然简单,也是可以完成的
var howdow = new Howdo();
// 这是页数,可以顺序也可以是乱序
var pages = [0, 1, 2, 3, 4, ...];
pages.forEach(function(page) {
// 分配任务
howdo.task(function(done) {
request(page, function(e, result) {
// 各种处理之后...,提交任务
done(e, result);
// 分页去查询,这些任务之间没有依赖关系
// 各个任务可以一起做
howdo.together(function(e, page0, page1, page2, page3, page4, ...) {
// 只要其中1个任务出现错误,都会返回错误
if(e) return callback(e);
// 参数顺序和分配任务时的顺序一致
// 这里得到了所有的分页数据
// 然后再拼接返回即可
var data = [].concat.apply([], [].slice.call(arguments, 1));
callback(null, data);
你可以看一下 async 模块的 map方法
CNode 社区为国内最专业的 Node.js 开源技术社区,致力于 Node.js 的技术研究。
服务器赞助商为
,存储赞助商为
,由提供应用性能服务。
新手搭建 Node.js 服务器,推荐使用无需备案的网页设计教程与开发
提供各种常见网页效果
提供各种各样的设计教程
装扮QQ,让QQ变得更酷
设计参考,提高自升水平
学习服务器和操作系统
提供各种素材和工具
收藏学习资料
您现在的位置:&&>>&&>>&&>>&&>>&正文
NodeJS中利用Promise来封装异步函数
&在写Node.js的过程中,连续的IO操作可能会导致“金字塔噩梦”,回调函数的多重嵌套让代码变的难以维护,利用CommonJs的Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦。
  Node.js提供的非阻塞IO模型允许我们利用回调函数的方式处理IO操作,但是当需要连续的IO操作时,你的回调函数会多重嵌套,代码很不美观,而且不易维护,而且可能会有许多错误处理的重复代码,也就是所谓的“Pyramid of Doom”。
step1(function (value1) {
&&& step2(value1, function(value2) {
&&&&&&& step3(value2, function(value3) {
&&&&&&&&&&& step4(value3, function(value4) {
&&&&&&&&&&&&&&& // Do something with value4
&&&&&&&&&&& });
&&&&&&& });
  这其实就是Node.js的Control flow的问题,对于这个问题,解决方案都许多,比如利用async,或者eventProxy等,不过本文的主题是利用CommonJs规范中对Promise来解决这个问题。
什么是Promise?
  CommonJs的Promise规范有许多种,我们一般讨论的是Promise/A+规范,它定义了Promise的基本行为。
  Promise是一个对象,它通常代表一个在未来可能完成的异步操作。这个操作可能成功也可能失败,所以一个Promise对象一般有3个状态:Pending,Fulfilled,Rejected。分别代表未完成、成功完成和操作失败。一旦Promise对象的状态从Pending变成Fulfilled或者Rejected任意一个,它的状态都没有办法再被改变。
  一个Promise对象通常会有一个then方法,这个方法让我们可以去操作未来可能成功后返回的值或者是失败的原因。这个then方法是这样子的:
promise.then(onFulfilled, onRejected)
  显而易见的是,then方法接受两个参数,它们通常是两个函数,一个是用来处理操作成功后的结果的,另一个是用来处理操作失败后的原因的,这两个函数的第一个参数分别是成功后的结果和失败的原因。如果传给then方法的不是一个函数,那么这个参数会被忽略。
  then方法的返回值是一个Promise对象,这一个特点允许我们链式调用then来达到控制流程的效果。这里有许多细节上的问题,比如值的传递或者错误处理等。Promise的规范是这样定义的:
onFulfilled或者onRejected函数的返回值不是Promise对象,则该值将会作为下一个then方法中onFulfilled的第一个参数,如果返回值是一个Promise对象,怎么then方法的返回值就是该Promise对象
onFulfilled或者onRejected函数中如果有异常抛出,则该then方法的返回的Promise对象状态转为Rejected,如果该Promise对象调用then,则Error对象会作为onRejected函数的第一个参数
如果Promise状态变为Fulfilled而在then方法中没有提供onFulfilled函数,则then方法返回的Promise对象状态变为Fulfilled且成功的结果为上一个Promise的结果,Rejected同理。
  补充一句,onFulfilled和onRejected都是异步执行的。
规范的实现:q
  上面讲的是Promise的规范,而我们需要的是它的实现,q是一个对Promise/A+有着较好实现规范的库。
  首先我们需要创建一个Promise对象,关于Promise对象创建的规范在Promise/B中,这里不做详细的解释,直接上代码。
&&& function(flag){
&&&&&&& var defer = q.defer();
&&&&&&& fs.readFile("a.txt", function(err, data){
&&&&&&& if(err) defer.reject(err);
&&&&&&&&&&& else defer.resolve(data);
&&&&&&&&&&& });
&&&&&&&&&&& return defer.
  多数Promise的实现在Promise的创建上大同小异,通过创建一个具有promise属性的defer对象,如果成功获取到值则调用defer.resolve(value),如果失败,则调用defer.reject(reason),最后返回defer的promise属性即可。这个过程可以理解为调用defer.resolve将Promise的状态变成Fulfilled,调用defer.reject将Promise的状态变成Rejected。
  在面对一系列连续的异步方法时,怎么利用Promise写出漂亮的代码呢?看下下面的例子。
&&& promise0.then(function(result){
&&&&&&& // dosomething
&&& }).then(function(result) {
&&&&&&& // dosomething
&&&&&&& return promise1;&&&
&&& }).then(function(result) {
&&&&&&& // dosomething
&&& }).catch(function(ex) {
&&&&&&& console.log(ex);
&&& }).finally(function(){
&&&&&&& console.log("final");
  在上面的代码中,then方法只接受OnFulfilled,而catch方法实际上就是then(null, OnRejected),这样的话只要一系列异步方法只要始终是成功返回值的,那么代码就会瀑布式的向下运行,如果其中任意一个异步方法失败或者发生异常,那么根据CommonJs的Promise规范,将执行catch中的function。q还提供了finally方法,从字面上也很好理解,就是不论resolve还是reject,最终都会执行finally中的function。
  看上去似乎不错,代码更以维护且美观了,那么如果希望并发呢?
&&&& q.all([promise0, promise1, promise2]).spread(function(val0, val1, val2){
&&&&&&&&&&&&&&&&&&& console.log(arguments);
&&&&&&&&&&&&&&& }).then(function(){
&&&&&&&&&&&&&&&&&&& console.log("done");
&&&&&&&&&&&&&&& }).catch(function(err){
&&&&&&&&&&&&&&&&&&& console.log(err);
&&&&&&&&&&&&&&& });
  q也为并发提供了api,调用all方法并传递一个Promise数组即可继续使用then的链式风格。还有像q.nfbind等可以将Node.js的原生API转化成Promise来统一代码格式也是挺好的。更多api在这里就不一一详述了。
  本文主要介绍通过使用Promise来解决Node.js控制流问题,但Promise也可同样应用于前端,EMCAScript6已经提供了原生的API支持。需要指出的是Promise并不是唯一的解决方案,async也是一个很好的选择,并且提供更友好的并发控制API,不过我觉得Promise在封装具有异步方法的函数时更具优势。
好了,本文就先到这里了,希望对大家能够有所帮助。
转载请注明:破洛洛(谢谢合作)
上一篇文章: 下一篇文章:
网友评论:
[][][][][][][][][][]如何理解NODEJS的回调函数中的上下文,这里需要用到闭包吗? - CNode技术社区
这家伙很懒,什么个性签名都没有留下。
今天对以前写的一段代码产生了疑惑,请问是不是在任何情况下,这两个值都相同?
exports.ts=function(data,callback){
var query={_id:data.id};
//var oldid=retextra(data.id);
Mydb.findOne(query,{},{},function(err,result){
console.log('Err:'+err);
return callback(err);
var now=new Date().getTime();
while(new Date().getTime() & now + 3000) {
console.log('传入值='+data.id+ ' 返回的值='+result._id);
//console.log('原来传入值通过闭包获取的值:'+oldid());
callback(err,result);
我的想法是当不断地调用这个函数,传入不同的id值,会导致在回调函数中data.id取到的值与result._id返回的值不相同,因为data的作用域与result的作用域不相同,当在回调函数中获取data时,可能取到的值不是那时传入的值。
我也试着浏览器多次调用这个方法,但总返回相同值。求解惑。
我的想法是先把data值包在闭包中,再在result中可获取当时的值。闭包函数:
function retextra(exdata) {
function getext() {
但现在也有点搞不清楚了,是不是不用闭包也可以了。
这是个好问题啊。
我也试着浏览器多次调用这个方法,但总返回相同值
因为本来就只会返回相同值
你每次调用 exports.ts 时,都会产生不同的闭包来保存 data 和 result。所以它们是互不影响的。
多次调用 exports.ts,调用之间互不影响
function(data,callback) 这一句创建了一个 data 局部变量。这个局部变量每次调用都产生一个新的,而不是跨调用存在的。
你好,我知道用闭包是不会有冲突的,但好像现在不用闭包也不会有问题了。我试着在一个循环中不断改变id值并调用这个方法,结果两个值也是一样的。
测试代码如下:
var myapi=require(’./action/myapi’);
server.get(’/ts’,myapi.ts);
//route设置
myapi.js中的方法:
exports.ts=function(req,res,next){
‘52e42467ea27’
,‘52ea3782533aee7’
,‘52ea3782533aee8’
,‘52ea3782533aee9’
,‘52ea3782533aeea’
,‘52e22fc7b25bb’
,‘52e22fac41fb25bc’
,‘52e23013b7ceecc00f8158f3’
,‘52e23016b7ceecc00f8158f4’
,‘52e23036b7ceecc00f8158f6’
for(var i=0;i&10;i++){
mydao.daots(so,function(err,result){
console.log(‘传入id=’+so.id+’ 返回的id=’+result._id); //不用闭包,这里的两个值确实不一样。
res.send(‘finished’);
return next();
mydao.js中的daots方法:
exports.daots=function(data,callback){
var query={_id:data.id};
FindingPerson.findOne(query,{},{},function(err,result){
console.log(‘Err:’+err);
return callback(err);
var now=new Date().getTime();
while(new Date().getTime() & now + 3000) {
console.log(‘Dao层传入的id=’+data.id+ ’ Dao层返回的id=’+result._id); //结果返回中这里的两个值永远相同
callback(err,result);
每次你调用 mydao.js中的daots方法时,参数 data 都保存了对于该次调用的 so 的引用。
mydao.daots(so,function(err,result){
console.log(‘传入id=’+so.id+’ 返回的id=’+result._id); //不用闭包,这里的两个值确实不一样。
res.send(‘finished’);
return next();
中,‘传入id=’+ 的
却保存的是紧接着 for 循环下面那个 so 的引用。
console.log(‘传入id=’+so.id+’ 返回的id=’+result._id);
这一句中,
的值都是数组的最后一个值。
感谢alsotang的指导,整理一下。
其实当时我是因为没搞清楚这个回调函数的作用域的问题,导致对回调和匿名函数没有一个清晰的认识。
写清楚点应该是这样的,
myapi.js中的方法:
exports.ts=function(req,res,next){
‘52e42467ea27’
,‘52ea3782533aee7’
,‘52ea3782533aee8’
,‘52ea3782533aee9’
,‘52ea3782533aeea’
,‘52e22fc7b25bb’
,‘52e22fac41fb25bc’
,‘52e23013b7ceecc00f8158f3’
,‘52e23016b7ceecc00f8158f4’
,‘52e23036b7ceecc00f8158f6’
for(var i=0;i&10;i++){
mydao.daots(so,function(err,result){
//不用闭包,这里的两个值确实不一样。
console.log(‘传入id=’+so.id+’ 返回的id=’+result._id);
res.send(‘finished’);
return next();
,它的两个传入参数err,result,是由mydao.daots每次执行
//后都会提供的,会是具体的值,与循环多少次,谁调用都没关系,每次都会是新的值。
//由于js内部变量(即var定义的变量)作用域的关系,即在同一个函数中,var定义的变量可以被
//函数中任何地方引用到,它的作用域是整个函数,而不是只限制在大括号{}中
//这里定义的cb函数中可以引用外面的so的值,是因为这个function cb()被定义在了最外面的ts方
//法中,而so这个变量在虽然在for(var i=0;i&10;i++){ var so…这个循环体中定义,但因为它
//也是在这个function ts()这个方法中且在调用cb时so已经被定义出来,所以cb中也可以访问so.
function cb(err,result){
console.log('传入id='+so.id+' 返回的id='+result._id);
res.send('finished');
return next();
mydao.daots(so,cb);
但为什么调用cb()后,
console.log(‘传入id=’+so.id+’ 返回的id=’+result._id);
这两个值不一样呢,且大多数情况下 &传入的id=循环体的最后一个值呢“是因为node.js的事件机制,当执行完mydao.daots()后不会等待返回结果,而继续执行循环体,当node.js从mongodb中取出数据后会自动调用我们设置的cb()这个回调函数,而由于执行循环体中的速度快,所以大部分情况下,所有循环执行执行完了,也就是说so这个变量现在的数据是循环中最后一个了,所以调用cb函数时,里面要打印so值时,打印的会是当前循环中最后一个值。且由于result是回调函数自己带回来的参数,这个result是当时传进去的so产生的结果值,所以两个值会不一样,只有最后一个回调函数的so值会与result带回来的so值相同。
所以如果要在cb中得到当时传进去的so值,必须要用闭包,闭包也就是把当时每次要传给mydao.daots中的so保存起来,需要用到的时候再取出来。
mydao.js中的daots方法中,结果返回中这里的两个值永远相同
console.log(‘Dao层传入的id=’+data.id+ ’ Dao层返回的id=’+result._id);
是因为每次调用daots方法时,传入的data值都是新产生的,且在自己的匿名函数体内这个值不会被改变,所以不管回调函数什么时候来取,它的值永远也就是传入时的值。
CNode 社区为国内最专业的 Node.js 开源技术社区,致力于 Node.js 的技术研究。
服务器赞助商为
,存储赞助商为
,由提供应用性能服务。
新手搭建 Node.js 服务器,推荐使用无需备案的温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
君子务本,本立而道成。
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
同步方法比较符合我们一般的编程风格,要想获得异步方法的执行结果,不能通过方法返回值,而是通过回调函数,传递一个方法给异步方法,异步方法里面通过执行回调函数执行高层代码。二、异步编程需注意的几个问题for循环for循环中如果有异步方法调用,那么一定要注意,回调函数中的外部变量问题,举例如下:for(var i=0 ; i& 10 ;i++){
fs.readdir(".",function(){
console.log(i);
}执行代码,大部分情况下,会打印出10个10,因为执行fs.readdir方法时,不会立即执行,而是在for循环结束后,再执行队列里的readdir方法,而此时i已经变成10,同时队列里的方法也不是一个执行完才执行另一个,而是几乎同时激活,所以方法执行的结束时间不能保证。如果想保证执行顺序,请接着往下读。回调函数只能且必须执行一次讲解之前,请先看如下代码:function abc(arg,callback){
asyncMethod(function(err,result)){
callback();
callback();
}对于这样的代码相信读者已经很熟悉,细心的朋友一眼就可以看出来如果err不为null,callback方法就执行了两次,但是不要小看这个问题,这是初学者很容易犯的错误哦。明白了根源问题就很容易解决了,把第二次callback调用放到else块里。问题还没有完,平时我们编写java方法有很多try catch,if else之类控制,往往在方法最下面添加一个return(没在意?因为你不写,IDE就提示错误了嘛),不能不说这种方法不好,但在nodejs的回调函数是不是同样适用呢,当然可以,个人建议是依然按传统思路写,但是在所有的执行callback()前或后加上语句停止往下执行即可,同时要注意,如果执行callback方法后还有要执行的操作,就不能这么写,总之记住一个原则:回调函数只能且必须执行一次!修改如下:function abc(arg,callback){
asyncMethod(function(err,result)){
return callback();
callback();
}不是所有的方法都是异步的不要受Node.js异步编程风格的影响,把自己开发的很多小方法都加上回调函数,不要自作多情了,不是随便写一个方法都是异步方法的,所以如果你的方法里没有其他的异步函数或者异步函数与外部调用无关,就放心大胆的return吧!三、多层回调函数解决方案Node.js的异步调用不能说不好,但是如果业务发杂且必须保证执行顺序,那么会有很多的回调函数,导致代码及逻辑很复杂,那么如何保证Node.js异步方法的同步调用呢,最常用的就是async库,下列举几个常用的操作:解决for循环问题var async = require('async');
async.forEach(array, function(item, callback) {
log('1.1 enter: ' + item.name);
setTimeout(function(){
callback(null, item.name);
}, item.delay);
}, function(err) {
log('1.1 err: ' + err);
});forEach方法能保证里面的调用顺序,只有执行了执行了回调函数callback才会执行下一个元素的调用。简单吧!多个异步方法都实行完才进行操作async.parallel({
a: function(cb) { asyncMethod1(function(err,result){cb(err,result);}) },
b: function(cb) { asyncMethod2(function(err,result){cb(err,result);}) }
}, function(err, results) {
log('1.3 err: ', err);
log('1.3 results: ', results);多个方法同时执行,同时都把返回结果放到results里,如果有一个方法执行失败,立即停止所有方法执行,如果全正确,results.a即使asyncMethod1的返回结果,results.b 即使asyncMethod2的返回结果。async还提供了其他一些有用的操作用来解决异步编程遇到的各种问题,具体请githu里的Demo:&原创文章,转载请注明出处:
阅读(6932)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'深入理解Node.js的异步编程风格',
blogAbstract:'概述Node.js异步编程是Node.js与常规javascript开发最大的不同,给出初学者带来了很大的困扰,同时异步编程模型也是Node.js快速响应的基础,利用好Node.js异步编程,会给开发带来很大便利,本文的目的是带领初学者了解并深入Node.js的异步编程,同时对有一定经验的同学也有一定的指导意义。',
blogTag:'node.js,async,异步编程风格',
blogUrl:'blog/static/4',
isPublished:1,
istop:false,
modifyTime:7,
publishTime:4,
permalink:'blog/static/4',
commentCount:3,
mainCommentCount:3,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:true,
hostIntro:'君子务本,本立而道成。',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}}

我要回帖

更多关于 nodejs 回调函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信