概述
Node.js异步编程是Node.js与常规javascript开发最大的不同,给出初学者带来了很大的困扰,同时异步编程模型也是Node.js快速响应的基础,利用好Node.js异步编程,会给开发带来很大便利,本文的目的是带领初学者了解并深入Node.js的异步编程,同时对有一定经验的同学也有一定的指导意义。
一、什么是异步编程
Node.js异步编程是指执行到一个方法的时候,不立即执行,而是加入到队列,然后接着往下执行,Node.js内部是适时执行队列里的方法,执行完执行回调方法(如果有的话)。由于是Node.js是单线程,异步编程模型保证了Node.js快速响应,充分利用CPU。
比如:在javascript中,setTimeout就是一个异步方法,执行到该方法时不会产生堵塞,到一定时间再执行。
举例:打印出一个目录中的文件信息:
//同步 filenames = fs.readdirSync("."); for (i = 0; i < filenames.length; i++) { console.log(filenames[i]); } console.log('Current uid: ' + process.getuid());//异步 fs.readdir(".", function (err, filenames) { var i; for (i = 0; i < filenames.length; i++) { console.log(filenames[i]); } }); console.log('Current uid: ' + process.getuid());
同步方法比较符合我们一般的编程风格,要想获得异步方法的执行结果,不能通过方法返回值,而是通过回调函数,传递一个方法给异步方法,异步方法里面通过执行回调函数执行高层代码。
二、异步编程需注意的几个问题
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)){ if(err){ callback(); } callback(); } }
对于这样的代码相信读者已经很熟悉,细心的朋友一眼就可以看出来如果err不为null,callback方法就执行了两次,但是不要小看这个问题,这是初学者很容易犯的错误哦。明白了根源问题就很容易解决了,把第二次callback调用放到else块里。
问题还没有完,平时我们编写java方法有很多try catch,if else之类控制,往往在方法最下面添加一个return(没在意?因为你不写,IDE就提示错误了嘛),不能不说这种方法不好,但在nodejs的回调函数是不是同样适用呢,当然可以,个人建议是依然按传统思路写,但是在所有的执行callback()前或后加上return;语句停止往下执行即可,同时要注意,如果执行callback方法后还有要执行的操作,就不能这么写,总之记住一个原则:回调函数只能且必须执行一次!修改如下:
function abc(arg,callback){ asyncMethod(function(err,result)){ if(err){ 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: https://github.com/bsspirit/async_demo
原创文章,转载请注明出处:http://lishaofengstar.blog.163.com/blog/static/1319728522013102665718744/