博客
关于我
Node.js 异步模式浅析
阅读量:792 次
发布时间:2023-02-16

本文共 4167 字,大约阅读时间需要 13 分钟。

注:此文是node.js实战读后的总结。

在平常的脚本语言中都是同步进行的,比如php,服务器处理多个请求的方法就是并行这些脚本。多任务处理,多线程等等。但是这种处理方式也有一个问题:每一个进程或者线程都会耗费大量的系统资源。如果有一种方法可以最大化的利用CPU的计算能力和可用内存以减少资源浪费那就极好了。这样,我们的node.js就应运而生了。

 

上一个node.js最简单的异步编程案例:

1 var fs = require('fs'); 2  3 var file; 4  5 fs.open( 6     'info.txt','r', 7     function(err,handle){ 8         var buf = new Buffer(100000); 9     fs.read(10     handle,buf,0,100000,null,11     function(err,length){12         console.log(buf.toString('utf8',0,length));13         fs.close(handle,function(){});14     });15         16     }17 );
View Code

从这个例子我们就可以看到在异步函数中使用的最多的回调函数,这些回调函数至少包含一个参数,即最后操作的状态(成功还是失败),一般而言还有第二个参数,即最后操作返回的结果或信息(比如文件句柄,数据库连接,查询到的数据等),一些回调函数可能还包含更多的参数.假设err代表返回的状态参数,则该参数的值一般会有以下几种情况:1.null:表示操作成功,并且会有一个返回值(如果你需要的话).2.一个error对象的实例:通常人们习惯在error对象上添加code字段并且用message字段来保存错误信息(注:这种方式可以让我们写出的非阻塞代码更具可控性)。对上面的代码进行优化加上错误处理:

1 var fs = require('fs'); 2  3 var file; 4  5 fs.open( 6     'info.txt','r', 7     function(err,handle){ 8         //第一种错误处理方式 9         if(err)10         {11             console.log("ERROR:"+ err.code + "("+err.message+")");12             return;13         }14         var buf = new Buffer(100000);15     fs.read(16     handle,buf,0,100000,null,17     function(err,length){18         //第二种错误处理方式19         if(err){20              console.log("ERROR:"+err.code+"("+err.message+")");21         }else{22             console.log(buf.toString('utf8',0,length));23             fs.close(handle,function(){});24         }25         26     });27         28     }29 );
View Code

但是,在异步处理的过程中得注意this的用法以及函数作用域的变化,看下面的代码:

1 var fs = require('fs'); 2  3 function FileObject(){ 4     this.filename = ''; 5  6     this.file_exists = function(callback){ 7         console.log("About to open:"+this.filename); 8         fs.open(this.filename,'r',function(err,handle){ 9             if(err){10                 console.log("Can't open:"+ this.filename);11                 callback(err);12                 return;13             }14             fs.close(handle,function(){});15             callback(null,true);16         });17     };18 }19 20 var fo = new FileObject();21 fo.filename = 'info';22 fo.file_exists(function(err,results){23     if(err){24         console.log("ERROR:"+err.code+"("+err.message+")");25         return;26     }27     console.log("file exists!!!");28 });
View Code

我们原本以为输出的应该是:

About to open:info

Can't open:info

但是实际上确实:

About to open:info

Can't open:undefined

ERROR:ENOENT(ENOENT, open 'G:\nodejs\info')

这是为什么呢?在我们的理解中,大多数情况下,当一个函数嵌套在另一个函数中时,他就会自动继承父函数的作用域,因而就能访问所有的变量了。但是为什么我们嵌套的回调函数却没有出现我们以为的输出呢?

这个问题得归结于this关键字和异步回调函数本身。在我们调用fs.open函数的时候,他会先初始化自己,然后调用底层的操作系统函数(在我们的代码中,就是打开文件),并且把回调函数插入到node.js的事件队列中去,执行完会立即返回给file_exists函数,然后退出。当fs.open完成任务后,node就会调用该回调函数,但此时,该函数已经不再拥有FileObject这个类的继承关系了,所以回调函数会重新赋予新的this指针,在这一过程中我们就丢失了我们的FileObject的this指针,故我们就不能访问我们的file_name。但是在这个过程中,回调函数的作用域还保留着。这里关系到nodejs的事件模式(参考资料:http://nodejs.org/docs/latest/api/events.html),这个也是nodejs的一个重要特性,我们现在不多说。这种错误最常见的解决方法就是把消失的this指针保存在变量中,下面我们来重写

1 this.file_exists = function(callback){ 2         //用一个变量来储存this指针 3         var self = this; 4         console.log("About to open:"+self.filename); 5         fs.open(this.filename,'r',function(err,handle){ 6             if(err){ 7                 console.log("Can't open:"+ self.filename); 8                 callback(err); 9                 return;10             }11             fs.close(handle,function(){});12             callback(null,true);13         });14     };
View Code

 

好了,这样我们的输出就和我们想象中的一样了,我们在写代码时一定不要忘了this的变化,不然就可能出现很多bug = = .

 

好,我们接着说.我们都知道Node运行在单线程中,使用事件轮询来调用外部函数和服务。它将回调函数插入事件队列中来等待响应,并且尽快执行回调函数。好,下面我们来看一个函数,这个函数的功能就是计算两个数组的交叉元素:

1 function compute_intersection(arr1,arr2,callback){ 2     var results = []; 3     for(var i = 0; i
View Code

当我数组中的元素特别大时,该函数就会耗费大量的计算时间。我们知道在单线程模式中,node.js在同一时间只能做一件事,所以这耗费的大量时间将会成为一个问题。而比如其他一些计算哈希。摘要(digest)或者其他一下耗费时间的操作就可能导致应用处于假死的状态。所以说node.js并不适合计算服务器,nodejs更适合常见的网络任务,比如那些需要大量I、O或者需要向其他服务请求的任务,如果需要一个大量计算的服务器,第一个解决方法就是把这些操作迁移到其他服务器上去,然后用nodejs远程调用。但如果只是偶尔执行这种任务,那么还有第二种解决方法,那就是利用全局对象process的nextTick方法,该方法的作用就是告诉系统,我不要执行控制权,你在你空闲的时候执行我给你的函数就行了.

 

好,异步中的一些陷井和基本已经介绍完了,欢迎大家补充。

 

转载于:https://www.cnblogs.com/xiaofanguoguo/p/4027999.html

你可能感兴趣的文章
Nginx配置实例-反向代理实例:根据访问的路径跳转到不同端口的服务中
查看>>
Nginx配置实例-负载均衡实例:平均访问多台服务器
查看>>
Nginx配置文件nginx.conf中文详解(总结)
查看>>
nginx配置详解、端口重定向和504
查看>>
Nginx配置负载均衡到后台网关集群
查看>>
Nginx配置限流,技能拉满!
查看>>
Nginx面试三连问:Nginx如何工作?负载均衡策略有哪些?如何限流?
查看>>
Nginx:NginxConfig可视化配置工具安装
查看>>
ngModelController
查看>>
ngrok | 内网穿透,支持 HTTPS、国内访问、静态域名
查看>>
ngrok内网穿透可以实现资源共享吗?快解析更加简洁
查看>>
NHibernate学习[1]
查看>>
NHibernate异常:No persister for的解决办法
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_日期类型_以及null数据同步处理补充---大数据之Nifi工作笔记0057
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_补充_更新时如果目标表中不存在记录就改为插入数据_Postgresql_Hbase也适用---大数据之Nifi工作笔记0059
查看>>
NIFI1.21.0_NIFI和hadoop蹦了_200G集群磁盘又满了_Jps看不到进程了_Unable to write in /tmp. Aborting----大数据之Nifi工作笔记0052
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_增删改数据分发及删除数据实时同步_通过分页解决变更记录过大问题_02----大数据之Nifi工作笔记0054
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表或全表增量同步_实现指定整库同步_或指定数据表同步配置_04---大数据之Nifi工作笔记0056
查看>>
NIFI1.23.2_最新版_性能优化通用_技巧积累_使用NIFI表达式过滤表_随时更新---大数据之Nifi工作笔记0063
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_根据binlog实现数据实时delete同步_实际操作04---大数据之Nifi工作笔记0043
查看>>