node.js的事件机制

首先, 补充下对node 的理解:

nodeJs 是一个单进程单线程应用程序, 但是通过事件和回调支持并发, 所以性能非常高~

那么什么是单进程单线程呢~(写给语文跟我一样不好的小伙伴)

我们来看下单进程和多进程的区别:

1.  多进程的优势在于任务的独立性,比如某个任务单独作为一个进程的话,崩溃只影响自己的服务,其他任务不受影响.如果是多个任务在同一个进程内部利用多个线程进行处理,某个线程发生了未处理的异常的话,会导致整个进程完蛋,所有的任务跟着遭殃

2.  从资源分配上来说,多进程方案比多线程方案更加灵活和自由

3. 不过任务间的通信方面多进程要比多线程复杂些,编一个好的多进程通信方案要比多线程间的通信方案困难多了(小伙伴们注意区分进程和线程哟~)

以web server为例的话,比如我的服务器上架设了三个网站,如果是用一个进程管理的话, 网站A遭受攻击死掉了,意味着另外两个网站会出现同样的现象. 如果是分开独立的进程的话,三个网站互不影响

具体来分呢, 单进程对比多进程有什么优点呢:

1)  初期实现起来比较简单快速, 而且不用考虑进程间通信的工作量

2) 单一性使得部署和运营比较简单(这还用说  / 白眼ing)

3) 内存占用少, 不过呢, 现在内存很廉价, 但是一分钱也是钱呀!

4) 进程内部通信效率比IPC/scoket(多进程数据通讯的终端)等要高效,  我一嗓子你就听见了, 就不用费力气装个电话了

当然, 肯定优缺点!不然花那么多钱开多进程的人也太蠢了 ~

单进程对比多进程的缺点~

1) 中后期随着业务逻辑的复杂化和需求的增加,这个单进程会变得臃肿, 难以维护。 一个任务分解成多个进程会使单个进程的逻辑简单,而不容易出错

2) 同进程内模块间是强依赖关系,需要在一起编译相互的影响也比较大。 这相对于多进程间通信来说, 耦合度较大(不符合高内聚低耦合的伟大思想), 不利于多团队并行开发。 多进程更便于多语言的协作开发。

3)任何模块的崩溃都将导致整个进程的失效,多进程模式更加稳定健壮,业务处理程序隔离运行, 一个go home不会影响其他(你敢崩我也崩);

4) 性能问题: 如果不支持进程间数据通讯的话,单进程的容量是受限的, 这个性能瓶颈对于支持群组类服务的尤其需要考虑。多进程部署极其灵活,可以扩充机器数量来提高系统处理性能,还可以从硬件上避免单点故障。(一个人承受不来)

5) 单进程中多线程难调试( 一枪开出去, 一群人倒了, 我还得检查一下谁中枪了才能给你debug)

举个小栗子

你有一个对象, 对象特别挑食, 但是对象只喜欢一种菜, 你每天做给她吃。

这就是个单进程单线程的模型,  如果你做的不好吃了, 对象不吃了。

但是我有一个对象, 她喜欢吃10种菜, 我每天端过去10份, 哪天其中某一份醋放多了, 对象说真难吃, 今天不吃了。这就是单进程多线程的模型。一个菜不好吃导致对象不吃了(全部线程崩掉)

..  如果我有两个对象..  每个对象喜欢吃一种菜

ok,  一个对象觉得好吃, 吃的脸圆圆的三下巴, 一个觉得不好吃常年不吃, 骨瘦如柴。

这就是多进程单线程互不影响的模型.. 多进程多线程我就不举栗了 ~

说到这里, 小伙伴有没有对单进程单线程有一些理解呢。nodeJs 就是单进程单线程的应用程序, 进程间互不影响, 绑定多个事件可以同时触发~  不用等你完了我再有动作, 所以nodeJs性能很高。

nodeJs 单线程类似进入一个while(true ) 的事件循环, 知道没有事件观察者的时候退出(每绑定一个事件, 就生成一个事件观察者), 当有事件发生的时候, 就会调用该事件的回调函数。

事件驱动程序

nodeJs 使用事件驱动模型(稍后说), 当web server 接收到请求时, 就把它关闭然后进行处理, 然后去服务下一个web 请求。可以理解成我触发事件, 就先关闭这个事件驱动, 然后处理, 我觉得是在防止二次触发~ 造成不正确的负载和意料之外的结果,这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)

可能有的小伙伴会问了, 什么是 事件驱动模型呢~

其实在了解事件驱动之前, 我们可以先看一下事件驱动的三大要素:

1) 事件源:  谁来接受外部事件

2) 侦听器:  能够接收事件源通知的对象

3) 事件处理程序:  用于处理事件

好, 包含以上三点的就是一个完整的事件驱动程序。

举个栗子

如果有一天你走在路上一不小心被天上掉下来的花瓶砸到了,并且晕死了过去。那么整个过程其实就是一个事件处理流程,而且我们可以非常方便的分析出刚才所提到的事件驱动模型中的三大要素。

1.被砸晕的这个人其实就是事件源,因为他是能够接受到外部的事件的源体。

2.侦听器就是这个人的大脑神经,因为它会感知到疼痛。

3.事件处理就是这个人晕死了过去。

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

说了那么多, 事件绑定怎么写呢。

在node 里, 有个事件模块 events,  我们可以通过实例化的events 对象 来绑定事件。

上代码:

var event = require('events'); // 引入事件模块
var eventObj = new event(); // 实例化一个事件对象
eventObj.on('起床', function() { // 绑定事件和事件回调
console.log('洗脸刷牙');

});
eventObj.emit('起床'); // 触发事件的方法

结果是符合预期的。

但是有一点需要注意哦, 当你想移除事件的时候, 直接eventObj.removeListener(‘起床')

然后各种报错~

其实正确的移除事件的方法是这样的~

上代码:

var event = require('events'); // 引入事件模块
var eventObj = new event(); // 实例化一个事件对象
function getUp() {
console.log('洗脸刷牙');
}
eventObj.on('起床', getUp);
eventObj.emit('起床');
eventObj.removeListener('起床', getUp);
eventObj.emit('起床');

成功移除事件~

node 还提供了只触发单次事件的方法~

eventObj.once(‘起床', getUp)

还有移除全部事件呀~

eventObj.removeAllListeners(getUp), 像我这样指定了getUp的事件, 则移除所有触发该事件的监听器。

eventObj.setMaxListeners(n)  参数是一个数字, 因为node默认了绑定事件最多10个, 10个以上的时候会警告。 如果不想被警告就设置最大监听器个数咯~

eventObj.listeners(even)  返回指定事件的监听器数组

eventObj.emit(getUp, [arg1], [arg2], [...])

按照参数的顺序执行事件,  返回值是true或者false。有此监听事件就返回true。

不知道小伙伴们有没有疑惑,  我明明已经把events 模块加载过来了, 为什么还要实例化才能用呢~

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

为什么要这样做呢?原因有两点:

首先,具有某个实体功能的对象实现事件符合语义(可以自己命名了啊喂(#`O′) ), 事件的监听和发射都应该是一个对象的方法。

其次 JavaScript 的对象机制是基于原型的, 支持部分多重继承,继承 EventEmitter不会打乱对象原有的继承关系。

Node 应用程序是如何工作的?

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

接下来让我们来重新看下前面的实例,创建一个 input.txt ,文件内容如下:

123456123123

创建 main.js 文件,代码如下:

var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
 if (err){
 console.log(err.stack);
 return;
 }
 console.log(data.toString());});
console.log("程序执行完毕");

以上程序中 fs.readFile() 是异步函数用于读取文件。 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。

如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。

执行以上代码,执行结果如下:

123456123123

接下来我们删除 input.txt 文件,执行结果如下所示:

程序执行完毕Error: ENOENT, open 'input.txt'

因为文件 input.txt 不存在,所以输出了错误信息。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • 浅谈node的事件机制

    Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. 在nodejs的官方文档中,明确写出了node的一个特性是event-driven(事件驱动),可见其非常重要.查看源码,我们可知其事件机制为用js写的EventEmitter类,写得非常优雅且应用了发布/订阅模式. 通过实现一个简易的.具有发布/订阅模式的事件机制,以此来理清EventEmitter类的实现思

  • 详解Node.js中的事件机制

    前言 在前端编程中,事件的应用十分广泛,DOM上的各种事件.在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的. 通常js给我们的第一印象就是运行在客户端浏览器上面的脚本,通过node.js我们可以在服务端运行javascript. node.js是基于单线程无阻塞异步式的I/O,异步式的I/O指的是当遇到I/O操作的时候,线程不阻塞而是进行下面的操作,那么I/O操作完成之后,线程时如何知道该操作完成的呢? 当操作完成耗时的I/O操作之后,会以事件的形式通知I/O操

  • node.js的事件机制

    首先, 补充下对node 的理解: nodeJs 是一个单进程单线程应用程序, 但是通过事件和回调支持并发, 所以性能非常高~ 那么什么是单进程单线程呢~(写给语文跟我一样不好的小伙伴) 我们来看下单进程和多进程的区别: 1.  多进程的优势在于任务的独立性,比如某个任务单独作为一个进程的话,崩溃只影响自己的服务,其他任务不受影响.如果是多个任务在同一个进程内部利用多个线程进行处理,某个线程发生了未处理的异常的话,会导致整个进程完蛋,所有的任务跟着遭殃 2.  从资源分配上来说,多进程方案比多线

  • Node.js EventEmmitter事件监听器用法实例分析

    本文实例讲述了Node.js EventEmmitter事件监听器用法.分享给大家供大家参考,具体如下: Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列. events 模块只提供了一个对象: events.EventEmitter.EventEmitter 的核心就是事件触发与事件监听器功能的封装. 该模块已被node.js默认引,不需要使用require()显示引入. EventEmitter 对象如果在实例化时发生错误,会触发 'error' 事件.当添加新的监

  • 深入理解JS DOM事件机制

    1.事件流 html 元素触发事件的顺序. 2.事件冒泡IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档).3.事件捕获事件捕获的思想是不太具体的节点应该更早的接收到事件,而最具体的节点应该在最后接收到节点.事件捕获的用意在于事件到达预定目标之前捕获它. DOM事件流 "DOM2级事件流"规定的事件流包括三个阶段:事件捕获阶段.处于目标阶段和冒泡阶段.首先发生的是事件捕获,

  • JS内部事件机制之单线程原理

    任务队列 主线程:正在执行的代码,会生成函数调用栈. macro-task(宏任务,新名:task)包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering. micro-task(微任务,新名:jobs)包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性,队列中只能有一个) 任务分类 同步任务,语句只按

  • node.js中事件触发器events的使用方法实例分析

    本文实例讲述了node.js中事件触发器events的使用方法.分享给大家供大家参考,具体如下: node.js是基于事件驱动的,通过events,我们可以方便的创建事件,并通过触发事件来调用我们自定义的监听函数. 所有能触发事件的对象都应该是 EventEmitter 类的实例,一般我们自定义一个类继承于 EventEmitter 类. 通过on()方法我们可以绑定事件与监听函数: const Events = require('events'); //自定义一个类,继承于EventEmitt

  • Node.js的模块化机制和Buffer对象详解

    目录 一.Node.js的模块化机制 1.什么是CommonJS模块规范 2.CommonJS模块规范化的内容 3.每个导出的模块都有一个moudle对象,该对象包含的属性有: 4.使用require导入模块 二.Buffer对象 1.用来弥补JavaScript在二进制数据处理上不足. 2.Buffer是一个构造函数 3.Buffer的基本操作: 4.Buffer类: 总结 一.Node.js的模块化机制 Node应用是由模块组成的,Node遵循了CommonJS的模块规范,来隔离每个模块的作

  • Node.js 的 GC 机制详解

    V8 的内存限制 在一般的后端开发语言中,在基本的内存使用上没有什么限制,然而在 Node 中通过 JavaScript 使用内存时就会发现只能使用部分内存(64位系统下约为1.4GB,32位系统下约为0.7GB).在这样的限制下,将会导致 Node 无法直接操作大内存对象. 造成这个问题的主要原因在于 Node 的 JavaScript 执行引擎 V8. 在 V8 中,所有的 JavaScript 对象都是通过堆来进行分配的.Node 提供了 V8 中内存的使用量查看方法 process.me

  • Node.JS中事件轮询(Event Loop)的解析

    当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 对于一个网站,后台大多不需要进行复杂的计算,我们的程序大多时间花费在I/O读取上. 看到一个数据:IO操作可以比数据处理慢几个数量级.高端SSD固态硬盘的读取速度可以达到200mb-700mb/s;读取1000字节需要1.4微秒.而在此期间,2GHZ频率的CPU可以执行28000个指令处理周期.而网络数据的IO甚至更慢! NodeJS采用单线程非阻塞的架构解决老大难的IO问题 当采用多线程时,为每一个请求开启一个新的线程(Apac

  • node.js中watch机制详解

    几乎所有构建系统都选择使用watch机制来解决开发过程中需要反复生成构建后文件的问题,但在watch机制下,长期以来我们必须忍受修改完代码,保存完代码必须喝口茶才能刷新看看效果的问题.在这里我们尝试探讨为什么watch不是银弹,并尝试寻找一种更好的方案来解决这个问题. watch基于的事实 当一个文件修改,我们能知道其修改可能导致的文件修改,那么重新构建这些文件即可. 通常对于文件A,构建成文件B这种场景,这种对应关系是极好确定的.但现实场景下,构建过程往往不是那么简单.例如: 文件A + 文件

随机推荐