(八)nodejs循序渐进-事件驱动(进阶篇)

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。

这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式IO或者事件驱动IO)

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

 

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

 EventEmitter触发器

大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。

例如  net.Server 会在每次有新连接时触发事件 fs.ReadStream会在打开文件时触发事件,stream会在数据可读时触发事件。

所有能触发事件的对象都是 EventEmitter 类的实例。 这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上。。

当 EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用。 被调用的监听器返回的任何值都将会被忽略并丢弃。

一个简单的 EventEmitter 实例,绑定了一个监听器。 eventEmitter.on() 用于注册监听器, eventEmitter.emit() 用于触发事件。

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', () => {console.log('触发事件');
});
myEmitter.emit('event');

 

将参数和 this 传给监听器  

eventEmitter.emit() 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时, this 关键词会被指向监听器所绑定的 EventEmitter 实例。

const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', function(a, b) {console.log(a, b, this, this === myEmitter); 
});
myEmitter.emit('event', 'a', 'b');

也可以使用 ES6 的箭头函数作为监听器。但 this 关键词不会指向 EventEmitter 实例: 

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.on('event', (a, b) => {console.log(a, b, this, this ,this===myEmitter); 
});
myEmitter.emit('event', 'a', 'b');

结果输出:

a b {} {} false 

异步 VS 同步

EventEmitter 以注册的顺序同步地调用所有监听器。 这样可以确保事件的正确排序,并有助于避免竞态条件和逻辑错误。 当适当时,监听器函数可以使用 setImmediate() 和 process.nextTick() 方法切换到异步的操作模式:

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.on('event', (a, b) => {setImmediate(() => {console.log('异步地发生');});
});
myEmitter.emit('event', 'a', 'b');

仅处理事件一次

当使用 eventEmitter.on() 注册监听器时,监听器会在每次触发命名事件时被调用。

const events = require('events');const myEmitter = new events.EventEmitter(); 
let m = 0;
myEmitter.on('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 打印: 2

使用 eventEmitter.once() 可以注册最多可调用一次的监听器。 当事件被触发时,监听器会被注销,然后再调用。

const events = require('events');const myEmitter = new events.EventEmitter(); 
let m = 0;
myEmitter.once('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 不触发

错误事件

当 EventEmitter 实例出错时,应该触发 'error' 事件。 这些在 Node.js 中被视为特殊情况。

如果没有为 'error' 事件注册监听器,则当 'error' 事件触发时,会抛出错误、打印堆栈跟踪、并退出 Node.js 进程。

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.emit('error', new Error('错误信息'));
// 抛出错误并使 Node.js 崩溃。

为了防止崩溃 Node.js 进程,通过使用符号 errorMonitor 安装监听器,可以监视 'error' 事件但不消耗触发的错误。

const events = require('events');const myEmitter = new events.EventEmitter(); 
myEmitter.on(EventEmitter.errorMonitor, (err) => {MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('错误'));
// 仍然抛出错误并使 Node.js 崩溃。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode304. 二维区域和检索 - 矩阵不可变

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。 上图子矩阵左上角 (row1, col1) (2, 1) ,右下角(row2, col2) (4, 3),该子矩形内元素的总和为 8。 示例…

(九)nodejs循序渐进-Express框架(进阶篇)

Express 框架 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。 使用 Express 可以快速地搭建一个完整功能的网站。 Express 框架核心特性: 可以设置中间件来响应 HTTP 请求。 定…

leetcode326. 3的幂 如此6的操作你想到了吗

给定一个整数,写一个函数来判断它是否是 3 的幂次方。 示例 1: 输入: 27 输出: true 示例 2: 输入: 0 输出: false 示例 3: 输入: 9 输出: true 示例 4: 输入: 45 输出: false 进阶: 你能不使用循环或者递归来完成本题吗? 注意最后一句…

(十)nodejs循序渐进-高性能游戏服务器框架pomelo之介绍和安装篇

目录 Pomelo 安装Pomelo 创建demoserver项目 pomelo命令 项目结构说明 pomelo框架 架构 服务器实现 客户端请求与响应、广播的抽象介绍 Pomelo pomelo是一个快速、可扩展、Node.js分布式游戏服务器框架,对游戏服务器开发感兴趣的同学可以关注关注。 之前…

(十二)nodejs循序渐进-高性能游戏服务器框架pomelo之创建一个游戏聊天服务器

上个章节我们简单介绍了下pomelo的安装和目录结构,有读者可能觉得有点吃不消,为什么不再深入讲一讲目录结构和里边的库,这里我就不费口舌了,大家可以去官网参考文档说明,本文只告诉大家如何利用这个框架来开发自己的东…

(十三)nodejs循序渐进-高性能游戏服务器框架pomelo之扩展聊天服务器为机器人自动聊天

聊天服务器扩展 大家在上一篇文章里相信已经学会了pomelo框架的基本用法了,那么我们在上一篇文章的代码基础上继续扩展,丰富系统,另外也熟悉下他的更多的用法,这一节我将扩展它:增加一个机器人自动聊天的功能。 目的…

leetcode1290. 二进制链表转整数 刷新认知,最简单算法题

给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。 请你返回该链表所表示数字的 十进制值 。 示例 1: 输入:head [1,0,1] 输出:5 解释:二进制数 (101) 转化为十进…

Redis:02---安装Redis(Linux+Windows+Docker)

Linux安装:一、安装方式1(下载源码编译安装)第一步:从下面的网址中下载Redis最新稳定版本的源代码sudo wget http://download.redis.io/redis-stable.tar.gz第二步:下载完之后解压,建立一个软链接指向于red…

C++: 06---构造函数析构函数

拷贝构造函数: 用一个已经存在的对象来生成一个相同类型的新对象。(浅拷贝)默认的拷贝构造函数: 如果自定义了拷贝构造函数,编译器就不在生成默认的拷贝构造函数。 如果没有自定义拷贝构造函数,但在代码中用到了拷贝构造函数,编译器会生成默认…

C++:11---友元函数、友元类

一、友元(friend) 概念:通过友元,打破了类的封装性,可以访问类内的所有成员分类:友元函数、友元类二、友元函数 概念:友元函数是一个普通函数,不属于类,但需要在类内表明友元关系 友元函数可访问类内所有成员,但类不可以访问友元函数…

C++:12---运算符重载

一、概念 对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型重载的运算符是具有特殊名字的函数,该函数也有返回值、参数列表、函数体二、运算符重载的3种实现方式 成员函数:私有、公有、保护都可以友元函数:同上全局函数:只能访问公有的三、运算符重载的…

Redis:03---Redis的启动与配置参数大全

一、Redis的可执行文件当我们安装完Redis之后,src和/usr/local/bin目录下提供了下面这些可执行程序,我们称之为Redis Shell:redis-serverRedis服务器redis-cliRedis命令行客户端redis-benchmarkRedis性能测试工具redis-check-aofRedis AOF持久…

leetcode80. 删除排序数组中的重复项 II

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 示例 1: 给定 nums [1,1,1,2…

Redis:04---键的基本命令(上)

一、KEYS:全量遍历键KEYS pattern功能:用来获取此数据库中所有的键名注意事项:KEYS命令需要遍历Redis中的所有键,当键的数量较多时会影响性能,不建议在生产环境下使用支持glob风格通配符格式,见下表&#x…

(十三) 深入浅出TCPIP之setsockopt参数详解

在socket编程中我们会经常用到setsockopt这个函数,那么本节我们将对这个函数的参数和使用做说明: 首先看下函数原型: int setsockopt( int socket, int level, int option_name,const void *option_value, size_t ,ption_len); 第…

Redis:05---键的基本命令(下) 生存周期

一、设置键生存/过期时间生存时间(Time To Live,TTL):在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键过期时间(expire time):是一个UNIX时间戳,当键…

C++:13---多态和虚函数表

多态的意思为“以一个public基类的指针/引用,寻址一个派生类对象”。 “多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定。这是如何实现的呢?请看下面的程序,该程序演示了多态类对象存储空间的大小。 #in…

leetcode96. 不同的二叉搜索树 动归vs数学?

给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 示例: 输入: 3 输出: 5 解释: 给定 n 3, 一共有 5 种不同结构的二叉搜索树: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 …

Redis:06---数据库管理

一、服务器中的数据库Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中,db数组的每个项都是一个redis.h/redisDb结构,每个redisDb结构代表一个数据库:struct redisServer {// ...redisDb *db; // 一个数组&#…

在同一局域网下连接共享文件夹失败,提示:你不能访问共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问

1.尝试打开guest访问。 (1)使用键盘 win R 键,打开运行窗口,并输入 gpedit.msc 打开本地组策略编辑器窗口 (2)选择计算机配置------->管理模板-------->网络-------->Lanman工作站。 &#…