>웹 프론트엔드 >JS 튜토리얼 >EventEmitter를 사용하여 Node.js에서 이벤트를 처리하는 방법은 무엇입니까?

EventEmitter를 사용하여 Node.js에서 이벤트를 처리하는 방법은 무엇입니까?

青灯夜游
青灯夜游앞으로
2020-11-02 17:46:092605검색

EventEmitter를 사용하여 Node.js에서 이벤트를 처리하는 방법은 무엇입니까?

이 튜토리얼에서는 Node.js의 기본 EvenEmitter 클래스에 대해 알아봅니다. 과정을 마친 후에는 이벤트, EvenEmitter 사용 방법, 프로그램에서 이벤트 활용 방법을 이해하게 됩니다. 또한 EventEmitter 클래스가 다른 기본 모듈에서 확장되는 방법을 배우고 몇 가지 예를 통해 그 뒤에 숨겨진 원리를 이해하게 됩니다. EvenEmitter 类。学完后你将了解事件、怎样使用  EvenEmitter 以及如何在程序中利用事件。另外还会学习 EventEmitter 类从其他本地模块扩展的内容,并通过一些例子了解背后的原理。

推荐教程:node js教程

总之本文涵盖了关于 EventEmitter 类的所有内容。

什么是事件?

当今事件驱动的体系结构非常普遍,事件驱动的程序可以产生、检测和响应各种事件。

Node.js 的核心部分是事件驱动的,有许多诸如文件系统(fs)和  stream 这样的模块本身都是用 EventEmitter 编写的。

在事件驱动的编程中,事件(event) 是一个或多个动作的结果,这可能是用户的操作或者传感器的定时输出等。

我们可以把事件驱动程序看作是发布-订阅模型,其中发布者触发事件,订阅者侦听事件并采取相应的措施。

例如,假设有一个服务器,用户可以向其上传图片。在事件驱动的编程中,诸如上传图片之类的动作将会发出一个事件,为了利用它,该事件还会有 1 到 n 个订阅者。

在触发上传事件后,订阅者可以通过向网站的管理员发电子邮件,让他们知道用户已上传照片并对此做出反应;另一个订阅者可能会收集有关操作的信息,并将其保存在数据库中。

这些事件通常是彼此独立的,尽管它们也可能是相互依赖的。

什么是EventEmitter?

EventEmitter 类是 Node.js 的内置类,位于 events 模块。根据文档中的描述:

大部分的 Node.js 核心 API 都是基于惯用的异步事件驱动的体系结构所实现的,在该体系结构中,某些类型的对象(称为“发射器”)发出已命名事件,这些事件会导致调用 Function 对象(“监听器”)”

这个类在某种程度上可以描述为发布-订阅模型的辅助工具的实现,因为它可以用简单的方法帮助事件发送器(发布者)发布事件(消息)给 监听器(订阅者)。

创建 EventEmitters

话虽如此,但还是先创建一个 EventEmitter 更加实在。可以通过创建类本身的实例或通过自定义类实现,然后再创建该类的实例来完成。

创建 EventEmitter 对象

先从一个简单的例子开始:创建一个 EventEmitter,它每秒发出一个含有程序运行时间信息的事件。

首先从 events 模块中导入 EventEmitter 类:

const { EventEmitter } = require('events');

然后创建一个 EventEmitter

const timerEventEmitter = new EventEmitter();

用这个对象发布事件非常容易:

timerEventEmitter.emit("update");

前面已经指定了事件名,并把它发布为事件。但是程序没有任何反应,因为还没有侦听器对这个事件做出反应。

先让这个事件每秒重复一次。用 setInterval()  方法创建一个计时器,每秒发布一次 update 事件:

let currentTime = 0;

// 每秒触发一次 update 事件
setInterval(() => {
    currentTime++;
    timerEventEmitter.emit('update', currentTime);
}, 1000);

EventEmitter 实例用来接受事件名称和参数。把 update 作为事件名, currentTime 作为自程序启动以来的时间进行传递。

通过 emit() 方法触发发射器,该方法用我们提供的信息推送事件。准备好事件发射器之后,为其订阅事件监听器:

timerEventEmitter.on('update', (time) => {
    console.log('从发布者收到的消息:');
    console.log(`程序已经运行了 ${time} 秒`);
});

通过 on() 方法创建侦听器,并传递事件名称来指定希望将侦听器附加到哪个事件上。 在 update 事件上,运行一个记录时间的方法。

on() 函数的第二个参数是一个回调,可以接受事件发出的附加数据。

运行代码将会输出:

从发布者收到的消息:
程序已经运行了 1 秒
从发布者收到的消息:
程序已经运行了 2 秒
从发布者收到的消息:
程序已经运行了 3 秒
...

如果只在事件首次触发时才需要执行某些操作,也可以用 once() 方法进行订阅:

timerEventEmitter.once('update', (time) => {
    console.log('从发布者收到的消息:');
    console.log(`程序已经运行了 ${time} 秒`);
});

运行这段代码会输出:

从发布者收到的消息:
程序已经运行了 1 秒

EventEmitter 与多个监听器

下面创建另一种事件发送器。这是一个计时程序,有三个侦听器。第一个监听器每秒更新一次时间,第二个监听器在计时即将结束时触发,最后一个在计时结束时触发:

  • update:每秒触发一次
  • end:在倒数计时结束时触发
  • end-soon
  • 추천 튜토리얼: node js 튜토리얼

간단히 말해서 이 문서에서는 EventEmitter 클래스에 대한 모든 내용을 다룹니다.

이벤트란 무엇인가요?

🎜이벤트 중심 아키텍처는 오늘날 매우 일반적입니다. 이벤트 중심 프로그램은 다양한 이벤트를 생성하고 감지하고 응답할 수 있습니다. 🎜🎜Node.js의 핵심 부분은 이벤트 중심이며 파일 시스템(fs) 및 stream과 같은 많은 모듈 자체가 EventEmitter코드를 사용합니다. > 썼다. 🎜🎜이벤트 기반 프로그래밍에서 이벤트는 하나 이상의 작업(사용자 작업 또는 센서의 타이밍 출력 등)의 결과입니다. 🎜🎜이벤트 중심 프로그램을 게시자가 이벤트를 트리거하고 구독자가 이벤트를 듣고 적절한 조치를 취하는 게시-구독 모델로 생각할 수 있습니다. 🎜🎜예를 들어, 사용자가 이미지를 업로드할 수 있는 서버가 있다고 가정해보세요. 이벤트 중심 프로그래밍에서 이미지 업로드와 같은 작업은 이벤트를 발생시키며, 이를 활용하기 위해 1~n명의 구독자를 갖게 됩니다. 🎜🎜업로드 이벤트가 트리거된 후 구독자는 사이트 관리자에게 이메일을 보내 사이트 관리자에게 알리고 대응할 수 있습니다. 다른 구독자는 해당 작업에 대한 정보를 수집하여 데이터베이스에 저장으로 보낼 수 있습니다. 🎜🎜이러한 이벤트는 일반적으로 서로 독립적이지만 상호의존적일 수도 있습니다. 🎜

EventEmitter란 무엇입니까?

🎜 EventEmitter 클래스는 events 모듈에 있는 Node.js의 내장 클래스입니다. 문서의 설명에 따르면: 🎜
대부분의 Node.js 핵심 API는 관용적 비동기 이벤트 기반 아키텍처를 기반으로 구현됩니다. 여기서 특정 유형의 개체("방출체"라고 함)는 명명된 이벤트를 발생시킵니다. 함수 객체 호출("리스너")"
🎜이 클래스는 이벤트 방출자(게시자)를 도울 수 있기 때문에 어느 정도 게시-구독 모델 구현을 위한 보조 도구로 설명될 수 있습니다. ) 간단한 방법으로 리스너(구독자)에게 이벤트(메시지)를 게시합니다. 🎜

EventEmitters 만들기

🎜 그렇다면 먼저 EventEmitter를 만들어 보겠습니다. 클래스 자체의 인스턴스를 생성하거나 사용자 정의 클래스를 구현한 다음 클래스의 인스턴스를 생성합니다. 🎜

EventEmitter 객체를 생성합니다.

🎜간단한 예제로 시작합니다. 매초마다 프로그램 런타임 정보가 포함된 이벤트를 내보내는 EventEmitter를 만듭니다. 🎜🎜먼저 events 모듈로 시작하세요. EventEmitter 클래스를 가져옵니다. 🎜
const countDown = (countdownTime) => {
    const eventEmitter = new EventEmitter();

    let currentTime = 0;

    // 每秒触发一次 update 事件
    const timer = setInterval(() => {
        currentTime++;
        eventEmitter.emit('update', currentTime);

        // 检查计时是否已经结束
        if (currentTime === countdownTime) {
            clearInterval(timer);
            eventEmitter.emit('end');
        }

        // 检查计时是否会在 2 秒后结束
        if (currentTime === countdownTime - 2) {
            eventEmitter.emit('end-soon');
        }
    }, 1000);
    return eventEmitter;
};
🎜 그런 다음 EventEmitter를 만듭니다. 🎜
const myCountDown = countDown(5);

myCountDown.on('update', (t) => {
    console.log(`程序已经运行了 ${t} 秒`);
});

myCountDown.on('end', () => {
    console.log('计时结束');
});

myCountDown.on('end-soon', () => {
    console.log('计时将在2秒后结束');
});
🎜이 개체를 사용하여 이벤트를 게시하는 것은 매우 쉽습니다. 🎜
程序已经运行了 1 秒
程序已经运行了 2 秒
程序已经运行了 3 秒
计时将在2秒后结束
程序已经运行了 4 秒
程序已经运行了 5 秒
计时结束
🎜이벤트 이름은 이전에 지정되었으며 이벤트로 게시되지만 아무 일도 일어나지 않습니다. 🎜🎜먼저 setInterval() 메서드를 사용하여 타이머를 생성합니다. 프로세서는 매초마다 update 이벤트를 게시합니다. 🎜
const { EventEmitter } = require('events');

class CountDown extends EventEmitter {
    constructor(countdownTime) {
        super();
        this.countdownTime = countdownTime;
        this.currentTime = 0;
    }

    startTimer() {
        const timer = setInterval(() => {
            this.currentTime++;
            this.emit('update', this.currentTime);
    
            // 检查计时是否已经结束
            if (this.currentTime === this.countdownTime) {
                clearInterval(timer);
                this.emit('end');
            }
    
            // 检查计时是否会在 2 秒后结束
            if (this.currentTime === this.countdownTime - 2) {
                this.emit('end-soon');
            }
        }, 1000);
    }
}
🎜EventEmitter 인스턴스는 이벤트 이름과 매개변수를 받아들이는 데 사용되며 update를 이벤트 이름으로 사용하고 currentTime은 이후 시간으로 전달됩니다. 🎜🎜이미터는 우리가 제공한 정보로 이벤트 이미터를 준비하는 emit() 메서드를 통해 트리거됩니다. 그 후 이벤트 리스너를 구독하세요. 🎜
const myCountDown = new CountDown(5);

myCountDown.on('update', (t) => {
    console.log(`计时开始了 ${t} 秒`);
});

myCountDown.on('end', () => {
    console.log('计时结束');
});

myCountDown.on('end-soon', () => {
    console.log('计时将在2秒后结束');
});

myCountDown.startTimer();
🎜Create a on() 메서드를 통해 리스너를 연결하려는 이벤트를 지정하는 이벤트 이름을 전달합니다. 업데이트 이벤트에서 시간을 기록하는 메서드를 실행합니다. 🎜🎜on() 함수의 두 번째 매개변수는 이벤트에서 발생하는 추가 데이터를 받을 수 있는 콜백입니다. 🎜🎜코드를 실행하면 다음이 출력됩니다. 🎜
程序已经运行了 1 秒
程序已经运行了 2 秒
程序已经运行了 3 秒
计时将在2秒后结束
程序已经运行了 4 秒
程序已经运行了 5 秒
计时结束
🎜이벤트가 처음으로 트리거될 때 특정 작업만 수행해야 하는 경우 once() 메서드를 사용하여 구독할 수도 있습니다. 🎜
myCountDown.on('end-soon', () => {
    console.log('计时将在2秒后结束');
});
🎜이 코드를 실행하면 다음과 같이 출력됩니다: 🎜
myCountDown.addListener('end-soon', () => {
    console.log('计时将在2秒后结束');
});

EventEmitter with multiple listenings

🎜아래에서 또 다른 이벤트 이미터를 만듭니다. 이것은 세 명의 청취자가 있는 타이머 프로그램입니다. 첫 번째 리스너는 매초 시간을 업데이트하고, 두 번째 리스너는 타이머가 끝나려고 할 때 실행되며, 마지막 리스너는 타이머가 끝날 때 실행됩니다. 🎜
  • update: 매초 실행 한 번 🎜
  • end: 카운트다운 종료 시 트리거됨 🎜
  • end-soon: 카운트다운 종료 2초 전에 트리거됨 🎜🎜🎜첫 번째 쓰기 이 이벤트 이미터 기능을 생성하는 방법: 🎜
    const countDown = (countdownTime) => {
        const eventEmitter = new EventEmitter();
    
        let currentTime = 0;
    
        // 每秒触发一次 update 事件
        const timer = setInterval(() => {
            currentTime++;
            eventEmitter.emit('update', currentTime);
    
            // 检查计时是否已经结束
            if (currentTime === countdownTime) {
                clearInterval(timer);
                eventEmitter.emit('end');
            }
    
            // 检查计时是否会在 2 秒后结束
            if (currentTime === countdownTime - 2) {
                eventEmitter.emit('end-soon');
            }
        }, 1000);
        return eventEmitter;
    };

    这个函数启动了一个每秒钟发出一次 update 事件的事件。

    第一个 if 用来检查计时是否已经结束并停止基于间隔的事件。如果已结束将会发布 end 事件。

    如果计时没有结束,那么就检查计时是不是离结束还有 2 秒,如果是则发布 end-soon 事件。

    向该事件发射器添加一些订阅者:

    const myCountDown = countDown(5);
    
    myCountDown.on('update', (t) => {
        console.log(`程序已经运行了 ${t} 秒`);
    });
    
    myCountDown.on('end', () => {
        console.log('计时结束');
    });
    
    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });

    这段代码将会输出:

    程序已经运行了 1 秒
    程序已经运行了 2 秒
    程序已经运行了 3 秒
    计时将在2秒后结束
    程序已经运行了 4 秒
    程序已经运行了 5 秒
    计时结束

    扩展 EventEmitter

    接下来通过扩展 EventEmitter 类来实现相同的功能。首先创建一个处理事件的 CountDown 类:

    const { EventEmitter } = require('events');
    
    class CountDown extends EventEmitter {
        constructor(countdownTime) {
            super();
            this.countdownTime = countdownTime;
            this.currentTime = 0;
        }
    
        startTimer() {
            const timer = setInterval(() => {
                this.currentTime++;
                this.emit('update', this.currentTime);
        
                // 检查计时是否已经结束
                if (this.currentTime === this.countdownTime) {
                    clearInterval(timer);
                    this.emit('end');
                }
        
                // 检查计时是否会在 2 秒后结束
                if (this.currentTime === this.countdownTime - 2) {
                    this.emit('end-soon');
                }
            }, 1000);
        }
    }

    可以在类的内部直接使用 this.emit()。另外 startTimer() 函数用于控制计时开始的时间。否则它将在创建对象后立即开始计时。

    创建一个 CountDown 的新对象并订阅它:

    const myCountDown = new CountDown(5);
    
    myCountDown.on('update', (t) => {
        console.log(`计时开始了 ${t} 秒`);
    });
    
    myCountDown.on('end', () => {
        console.log('计时结束');
    });
    
    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    
    myCountDown.startTimer();

    运行程序会输出:

    程序已经运行了 1 秒
    程序已经运行了 2 秒
    程序已经运行了 3 秒
    计时将在2秒后结束
    程序已经运行了 4 秒
    程序已经运行了 5 秒
    计时结束

    on() 函数的别名是 addListener()。看一下 end-soon 事件监听器:

    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });

    也可以用 addListener() 来完成相同的操作,例如:

    myCountDown.addListener('end-soon', () => {
        console.log('计时将在2秒后结束');
    });

    EventEmitter 的主要函数

    eventNames()

    此函数将以数组形式返回所有活动的侦听器名称:

    const myCountDown = new CountDown(5);
    
    myCountDown.on('update', (t) => {
        console.log(`程序已经运行了 ${t} 秒`);
    });
    
    myCountDown.on('end', () => {
        console.log('计时结束');
    });
    
    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    
    console.log(myCountDown.eventNames());

    运行这段代码会输出:

    [ 'update', 'end', 'end-soon' ]

    如果要订阅另一个事件,例如 myCount.on('some-event', ...),则新事件也会添加到数组中。

    这个方法不会返回已发布的事件,而是返回订阅的事件的列表。

    removeListener()

    这个函数可以从 EventEmitter 中删除已订阅的监听器:

    const { EventEmitter } = require('events');
    
    const emitter = new EventEmitter();
    
    const f1 = () => {
        console.log('f1 被触发');
    }
    
    const f2 = () => {
        console.log('f2 被触发');
    }
    
    emitter.on('some-event', f1);
    emitter.on('some-event', f2);
    
    emitter.emit('some-event');
    
    emitter.removeListener('some-event', f1);
    
    emitter.emit('some-event');

    在第一个事件触发后,由于 f1f2 都处于活动状态,这两个函数都将被执行。之后从 EventEmitter 中删除了 f1。当再次发出事件时,将会只执行 f2

    f1 被触发
    f2 被触发
    f2 被触发

    An alias for removeListener() is off(). For example, we could have written:

    removeListener() 的别名是 off()。例如可以这样写:

    emitter.off('some-event', f1);

    removeAllListeners()

    该函数用于从 EventEmitter 的所有事件中删除所有侦听器:

    const { EventEmitter } = require('events');
    
    const emitter = new EventEmitter();
    
    const f1 = () => {
        console.log('f1 被触发');
    }
    
    const f2 = () => {
        console.log('f2 被触发');
    }
    
    emitter.on('some-event', f1);
    emitter.on('some-event', f2);
    
    emitter.emit('some-event');
    
    emitter.removeAllListeners();
    
    emitter.emit('some-event');

    第一个 emit() 会同时触发 f1f2,因为它们当时正处于活动状态。删除它们后,emit() 函数将发出事件,但没有侦听器对此作出响应:

    f1 被触发
    f2 被触发

    错误处理

    如果要在 EventEmitter 发出错误,必须用 error 事件名来完成。这是 Node.js 中所有 EventEmitter 对象的标准配置。这个事件必须还要有一个 Error 对象。例如可以像这样发出错误事件:

    myEventEmitter.emit('error', new Error('出现了一些错误'));

    error 事件的侦听器都应该有一个带有一个参数的回调,用来捕获 Error 对象并处理。如果 EventEmitter 发出了 error 事件,但是没有订阅者订阅 error 事件,那么 Node.js 程序将会抛出这个 Error。这会导致 Node.js 进程停止运行并退出程序,同时在控制台中显示这个错误的跟踪栈。

    例如在 CountDown 类中,countdownTime参数的值不能小于 2,否则会无法触发 end-soon 事件。在这种情况下应该发出一个 error 事件:

    class CountDown extends EventEmitter {
        constructor(countdownTime) {
            super();
    
            if (countdownTimer < 2) {
                this.emit(&#39;error&#39;, new Error(&#39;countdownTimer 的值不能小于2&#39;));
            }
    
            this.countdownTime = countdownTime;
            this.currentTime = 0;
        }
    
        // ...........
    }

    处理这个错误的方式与其他事件相同:

    myCountDown.on(&#39;error&#39;, (err) => {
        console.error('发生错误:', err);
    });

    始终对 error 事件进行监听是一种很专业的做法。

    使用 EventEmitter 的原生模块

    Node.js 中许多原生模块扩展了EventEmitter 类,因此它们本身就是事件发射器。

    一个典型的例子是 Stream 类。官方文档指出:

    流可以是可读的、可写的,或两者均可。所有流都是 EventEmitter 的实例。

    先看一下经典的 Stream 用法:

    const fs = require('fs');
    const writer = fs.createWriteStream('example.txt');
    
    for (let i = 0; i < 100; i++) {
      writer.write(`hello, #${i}!\n`);
    }
    
    writer.on(&#39;finish&#39;, () => {
      console.log('All writes are now complete.');
    });
    
    writer.end('This is the end\n');

    但是,在写操作和 writer.end() 调用之间,我们添加了一个侦听器。 Stream 在完成后会发出一个 finished 事件。在发生错误时会发出 error 事件,把读取流通过管道传输到写入流时会发出 pipe 事件,从写入流中取消管道传输时,会发出 unpipe 事件。

    另一个类是 child_process 类及其 spawn() 方法:

    const { spawn } = require('child_process');
    const ls = spawn('ls', ['-lh', '/usr']);
    
    ls.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });
    
    ls.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });
    
    ls.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
    });

    当  child_process 写入标准输出管道时,将会触发  stdoutdata 事件。当输出流遇到错误时,将从 stderr 管道发送 data 事件。

    마지막으로 프로세스가 종료된 후 close 이벤트가 트리거됩니다. close 事件。

    总结

    事件驱动的体系结构使我们能够创建高内聚低耦合的系统。事件表示某个动作的结果,可以定义 1个或多个侦听器并对其做出反应。

    本文深入探讨了 EventEmitter

    요약

    이벤트 중심 아키텍처를 사용하면 응집력은 높고 결합력은 낮은 시스템을 만들 수 있습니다. 이벤트는 작업의 결과를 나타내며 하나 이상의 리스너를 정의하고 이에 반응할 수 있습니다.

    이 문서에서는 EventEmitter 클래스와 해당 기능을 자세히 살펴봅니다. 인스턴스화하여 직접 사용하고 해당 동작을 사용자 정의 개체로 확장합니다.

    마지막으로 이 클래스의 몇 가지 중요한 기능을 소개합니다.

    더 많은 프로그래밍 관련 지식을 보려면 🎜프로그래밍 코스🎜를 방문하세요! ! 🎜

위 내용은 EventEmitter를 사용하여 Node.js에서 이벤트를 처리하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제