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

拼搏现实的明天。 2022-12-22 09:56 293阅读 0赞

事件驱动程序

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

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

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

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

59aedf941369ca945f8f91bd4ed29545.png

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(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 类来绑定和监听事件,如下实例:

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

将参数和 this 传给监听器

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

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

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

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

结果输出:

  1. a b {} {} false

异步 VS 同步

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

  1. const events = require('events');
  2. const myEmitter = new events.EventEmitter();
  3. myEmitter.on('event', (a, b) => {
  4. setImmediate(() => {
  5. console.log('异步地发生');
  6. });
  7. });
  8. myEmitter.emit('event', 'a', 'b');

仅处理事件一次

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

  1. const events = require('events');
  2. const myEmitter = new events.EventEmitter();
  3. let m = 0;
  4. myEmitter.on('event', () => {
  5. console.log(++m);
  6. });
  7. myEmitter.emit('event');
  8. // 打印: 1
  9. myEmitter.emit('event');
  10. // 打印: 2

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

  1. const events = require('events');
  2. const myEmitter = new events.EventEmitter();
  3. let m = 0;
  4. myEmitter.once('event', () => {
  5. console.log(++m);
  6. });
  7. myEmitter.emit('event');
  8. // 打印: 1
  9. myEmitter.emit('event');
  10. // 不触发

错误事件

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

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

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

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

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

发表评论

表情:
评论列表 (有 0 条评论,293人围观)

还没有评论,来说两句吧...

相关阅读

    相关 nodejs 事件驱动

    > 其实这是两部分内容 异步I/O  事件驱动  、 > > ——异步I/O就是nodejs是一个异步非阻塞语言  例如fs模块就能很好的理解。这里不多赘述 > > 今天主