要理解事件循环,首先要理解事件驱动编程(Event Driven Programming)。它出现在1960年。如今,事件驱动编程在UI编程中大量使用。JavaScript的一个主要用途是与DOM交互,所以使用基于事件的API是很自然的。简单地定义:事件驱动编程通过事件或状态的变化来进行应用程序的流程控制。一般通过事件监听实现,一旦事件被检测到(即状态改变)则调用相应的回调函数。听起来很熟悉?其实这就是node.js事件循环的基本工作原理。如果你熟悉客户端JavaScript的开发,想一想那些.on*()方法,如element.onclick(),他们用来与DOM元素相结合,传递用户交互。这个工作模式允许在单个实例上触发多个事件。Node.js通过EventEmitter(事件发生器)触发这种模式,如在服务器端的Socket和 “http”模块中,可以从一个单一实例触发一种或一种以上的状态改变。

  js是单线程,对于阻塞操作,js会封装参数和回调函数,交给底层去处理,也就是io线程池。线程池处理完毕会放到一个类似队列里面,然后node的事件循环线程会去获取这个队列的数据,执行回调。也就是说使用事件驱动模型的node.js,在当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)。

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

  如上图所示,每个异步函数执行结束后,都会在事件队列中追加一个事件(同时保存一些必要参数)。事件轮询下一次循环便可取出事件,然后会调用异步方法对应的回调函数(参数)。这样一来,nodejs便能保证开发者编写的每行代码(每个回调)均在主线程中执行。注意这里有一个问题,如果开发者在回调函数中调用了阻塞方法,那么整个事件轮询就会阻塞,事件队列中的事件得不到及时处理。正因为这样,node.js中的一些库方法均是异步的,也提倡用户调用异步方法。

  node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类(EventEmitter类会在下一笔记做分析)来绑定和监听事件,如下实例:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

  绑定事件处理程序:

// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

  触发事件:

// 触发事件
eventEmitter.emit('eventName');

  接下来就通过一个实例来实操一下。毕竟说得再多还不如动手试试。

  创建 demo3.js 文件,代码如下所示:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter(); // 创建事件处理程序
var connectHandler = function connected() {
console.log('连接成功。');
// 触发 data_received 事件
eventEmitter.emit('data_received');
} // 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler); // 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){
console.log('数据接收成功。');
}); // 触发 connection 事件
eventEmitter.emit('connection'); console.log("程序执行完毕。");

  执行node命令查看效果:

  

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

  下面我们再来看一个栗子。先创建一个txt文本文件,比如demo3_1.txt,文本内容如下:

this is a demo!

  再创建一个node脚本,比如叫demo3_1.js,代码如下:

//引入文件操作系统模块
var fs = require("fs");
//异步读取文件内容
fs.readFile('demo3_1.txt', function (err, data) {
//如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。
//如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。
if (err){
console.log(err.stack);
return;
}
console.log(data.toString());
}); console.log("程序执行完毕");  

  执行node命令查看效果:

  

  可以看到,脚本读取文件内容成功,所以不会报错。我们可以试着让它报错,比如把txt文件删除了,然后再来看看效果。

  由于文件 demo3_1.txt 不存在,所以输出了错误信息。

  上面可以看到,fs.readFile()方法的最后一个参数就是回调函数,而回调函数里面第一个参数就是回调函数接受的错误参数。

  关于node.js的事件循环机制,我还只是了解了点皮毛,如果有大神愿意指导,鄙人很愿意聆听(*^-^*)

node.js学习笔记(三)——事件循环的更多相关文章

  1. Node.js学习笔记(三) --- package.json 及cnpm

    一.包 Nodejs   中除了它自己提供的核心模块外,我们可以自定义模块,也可以使用第三方的模块.Nodejs 中第三方模块由包组成,可以通过包来对一组具有相互依赖关系的模块进行统一管理. 完全符合 ...

  2. 系列文章--Node.js学习笔记系列

    Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- 简介及安装Node.js开发环境 Nodejs学习笔记(二)--- 事件模块 Nodejs学习笔记(三)--- 模块 Nodejs学 ...

  3. 一点感悟:《Node.js学习笔记》star数突破1000+

    写作背景 笔者前年开始撰写的<Node.js学习笔记> github star 数突破了1000,算是个里程碑吧. 从第一次提交(2016.11.03)到现在,1年半过去了.突然有些感慨, ...

  4. Node.js学习笔记(2):基本模块

    Node.js学习笔记(2):基本模块 模块 引入模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在No ...

  5. Node.js学习笔记(3):NPM简明教程

    Node.js学习笔记(3):NPM简明教程 NPM常用操作 更新NPM版本 npm install npm -g -g,表示全局安装.我们可以指定更新版本,只需要在后面填上@版本号即可,也可以输入@ ...

  6. Node.js学习笔记(1):Node.js快速开始

    Node.js学习笔记(1):Node.js快速开始 Node.js的安装 下载 官方网址:https://nodejs.org/en/ 说明: 在Windows上安装时务必选择全部组件,包括勾选Ad ...

  7. Node.js学习笔记(4):Yarn简明教程

    Node.js学习笔记(4):Yarn简明教程. 引入Yarn NPM是常用的包管理工具,现在我们引入是新一代的包管理工具Yarn.其具有快速.安全.可靠的特点. 安装方式 使用npm工具安装yarn ...

  8. Node.js学习笔记(一)基础介绍

    什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...

  9. Node.js实战(九)之事件循环

    Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高. Node.js 几乎每一个 API 都是支持回调函数的. Node ...

随机推荐

  1. Python学习---Python的异步IO[all]

    1.1.1. 前期环境准备和基础知识 安装: pip3 install aiohttp pip3 install grequests pip3 install wheel pip3 install s ...

  2. Linux 系统必须掌握的文件_【all】

    0.Linux 系统文件的详解 1.Linux 系统的网络配置文件 2.Linux 系统的DNS配置文件 3.Linux 系统的IP与域名解析文件[局域网的DNS] 4.Linux 系统的主机别名文件 ...

  3. SpringBoot+MyBatis连接数据库

    SpringBoot通过MyBatis连接数据库有2种方法: 1.注解 2.XML文件 1.注解 1.构建项目 2.添加依赖: <dependencies> <dependency& ...

  4. SOA面向服务架构——SOA的概念

    SOA的概念是Gartner 在1996年提出来的,并于2002年12月进一步提出SOA是“现代应用开发领域最重要的课题”.   一.SOA的定义 SOA分为广义的SOA和狭义的SOA,广义的SOA是 ...

  5. foreach 计数

    foreach(var item in list.Select((x, i) => new { x, i })){    User user = item.x;    int Idx = ite ...

  6. laravel 资料

    1.http://maxoffsky.com/maxoffsky-blog/building-a-shop-with-laravel-tutorial-series-announcement/  一篇 ...

  7. Jenkins+Github(Robotframework代码)

    个人记录,且为Windows10系统,仅供参考. 一. 准备 1. Github:安装git.注册github.将Robotframework代码更新到github. 2. Jenkins安装,并安装 ...

  8. grep, sed 与 awk 补补课,到底怎么用!

    grep, sed 与 awk 相当有用 ! gerp 查找, sed 编辑, awk 根据内容分析并处理. awk(关键字:分析&处理) 一行一行的分析处理 awk '条件类型1{动作1}条 ...

  9. 初识Qt图片显示、平移及旋转

    1.新建一个Qt Gui应用,项目名称为myPicture,基类选择为QMainWindow,类名设置为MainWindow. 2.在mainwindow.h头文件中添加void paintEvent ...

  10. java常用API之Date类

    Date类: 类 Date 表示特定的瞬间,精确到毫秒. 毫秒概念:1000毫秒=1秒 毫秒的0点: System.currentTimeMillis()  返回值long类型参数   用于获取当前日 ...