要理解事件循环,首先要理解事件驱动编程(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. 【matlab】 QR分解 求矩阵的特征值

    "QR_H.m" function [Q,R] = QR_tao(A) %输入矩阵A %输出正交矩阵Q和上三角矩阵R [n,n]=size(A); E = eye(n); X = ...

  2. Spring 容器介绍

    Spring 框架的实现依赖 IoC (反向控制) 原则,更为形象的称呼是 DI (依赖注入).相对于 Bean 直接构造依赖的对象,Spring 框架则根据 Bean之间的依赖关系创建对象,并注入到 ...

  3. 【记录】有趣的python模块记录

    1. paramiko: 基于SSH用于连接远程服务器并执行相关操作,公钥私钥登录等等

  4. HBase TableExistsException: hbase:namespace

    这个报错一般存在于独立安装Zookeeper集群中. 报这个错的操作时这样的, 先停掉了了Hbase formatZK后重启Hbase 启动hbase shell 后HMaster挂掉,看log里就有 ...

  5. MySQL主从.md

    MySQL Replication 概述 Mysql内建的复制功能是构建大型,高性能应用程序的基础.将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它 ...

  6. Uva1395 POJ3522 Slim Span (最小生成树)

    Description Given an undirected weighted graph G, you should find one of spanning trees specified as ...

  7. 类型构造器-Functor[F[_]]

    类型构造是抽象类型或高阶类型实例化的过程: 类型构造器是任意输入一个或几个已有类型,能够生成新类型的类型: https://www.cnblogs.com/feng9exe/p/9925027.htm ...

  8. 「GXOI / GZOI2019」旅行者

    题目 我还是太傻了 考虑每一条边的贡献,对于一条有向边\((u,v,w)\),我们求出\(k\)个关键点中到\(u\)最近的距离\(dis_1\),以及\(v\)到\(k\)个关键点中最近的距离\(d ...

  9. 03.Java语言基础

    Java程序的组成 关键字,标识符,注释,变量,语句,表达式,数组,方法 关键字 Java语言内部使用了的一些用于特殊用途的词汇,那么在程序中用户不能使用.语言本身保留了一些词汇用于语言的语法等用途. ...

  10. regex_search

    在regex_match()里边只能看到regex和输入的字符串是不是全部匹配上了,匹配上了就返回true,否则false.然而他不能返回匹配到的子字符串:regex_search()和regex_m ...