From ec9e3f15a0cc196cbe7b2816f7e3b05068550674 Mon Sep 17 00:00:00 2001 From: Feng Yu Date: Sat, 28 May 2022 11:50:20 +0800 Subject: [PATCH] doc: add ESM version examples to events api doc --- doc/api/events.md | 363 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 345 insertions(+), 18 deletions(-) diff --git a/doc/api/events.md b/doc/api/events.md index 6cd5d47fdfe04a..310d9d07761ff5 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -30,7 +30,19 @@ The following example shows a simple `EventEmitter` instance with a single listener. The `eventEmitter.on()` method is used to register listeners, while the `eventEmitter.emit()` method is used to trigger the event. -```js +```mjs +import EventEmitter from 'node:events'; + +class MyEmitter extends EventEmitter {} + +const myEmitter = new MyEmitter(); +myEmitter.on('event', () => { + console.log('an event occurred!'); +}); +myEmitter.emit('event'); +``` + +```cjs const EventEmitter = require('node:events'); class MyEmitter extends EventEmitter {} @@ -160,7 +172,18 @@ myEmitter.emit('error', new Error('whoops!')); It is possible to monitor `'error'` events without consuming the emitted error by installing a listener using the symbol `events.errorMonitor`. -```js +```mjs +import { EventEmitter, errorMonitor } from 'node:events'; + +const myEmitter = new EventEmitter(); +myEmitter.on(errorMonitor, (err) => { + MyMonitoringTool.log(err); +}); +myEmitter.emit('error', new Error('whoops!')); +// Still throws and crashes Node.js +``` + +```cjs const { EventEmitter, errorMonitor } = require('node:events'); const myEmitter = new EventEmitter(); @@ -208,7 +231,19 @@ ee2[Symbol.for('nodejs.rejection')] = console.log; Setting `events.captureRejections = true` will change the default for all new instances of `EventEmitter`. -```js +```mjs +import events from 'node:events'; + +events.captureRejections = true; +const ee1 = new events.EventEmitter(); +ee1.on('something', async (value) => { + throw new Error('kaboom'); +}); + +ee1.on('error', console.log); +``` + +```cjs const events = require('node:events'); events.captureRejections = true; const ee1 = new events.EventEmitter(); @@ -237,7 +272,11 @@ changes: The `EventEmitter` class is defined and exposed by the `node:events` module: -```js +```mjs +import EventEmitter from 'node:events'; +``` + +```cjs const EventEmitter = require('node:events'); ``` @@ -337,7 +376,40 @@ to each. Returns `true` if the event had listeners, `false` otherwise. -```js +```mjs +import EventEmitter from 'node:events'; +const myEmitter = new EventEmitter(); + +// First listener +myEmitter.on('event', function firstListener() { + console.log('Helloooo! first listener'); +}); +// Second listener +myEmitter.on('event', function secondListener(arg1, arg2) { + console.log(`event with parameters ${arg1}, ${arg2} in second listener`); +}); +// Third listener +myEmitter.on('event', function thirdListener(...args) { + const parameters = args.join(', '); + console.log(`event with parameters ${parameters} in third listener`); +}); + +console.log(myEmitter.listeners('event')); + +myEmitter.emit('event', 1, 2, 3, 4, 5); + +// Prints: +// [ +// [Function: firstListener], +// [Function: secondListener], +// [Function: thirdListener] +// ] +// Helloooo! first listener +// event with parameters 1, 2 in second listener +// event with parameters 1, 2, 3, 4, 5 in third listener +``` + +```cjs const EventEmitter = require('node:events'); const myEmitter = new EventEmitter(); @@ -381,8 +453,23 @@ added: v6.0.0 Returns an array listing the events for which the emitter has registered listeners. The values in the array are strings or `Symbol`s. -```js +```mjs +import EventEmitter from 'node:events'; + +const myEE = new EventEmitter(); +myEE.on('foo', () => {}); +myEE.on('bar', () => {}); + +const sym = Symbol('symbol'); +myEE.on(sym, () => {}); + +console.log(myEE.eventNames()); +// Prints: [ 'foo', 'bar', Symbol(symbol) ] +``` + +```cjs const EventEmitter = require('node:events'); + const myEE = new EventEmitter(); myEE.on('foo', () => {}); myEE.on('bar', () => {}); @@ -757,7 +844,26 @@ promise rejection happens when emitting an event and It is possible to use [`events.captureRejectionSymbol`][rejectionsymbol] in place of `Symbol.for('nodejs.rejection')`. -```js +```mjs +import { EventEmitter, captureRejectionSymbol } from 'node:events'; + +class MyClass extends EventEmitter { + constructor() { + super({ captureRejections: true }); + } + + [captureRejectionSymbol](err, event, ...args) { + console.log('rejection happened for', event, 'with', err, ...args); + this.destroy(err); + } + + destroy(err) { + // Tear the resource down here. + } +} +``` + +```cjs const { EventEmitter, captureRejectionSymbol } = require('node:events'); class MyClass extends EventEmitter { @@ -853,7 +959,24 @@ the emitter. For `EventTarget`s this is the only way to get the event listeners for the event target. This is useful for debugging and diagnostic purposes. -```js +```mjs +import { getEventListeners, EventEmitter } from 'node:events'; + +{ + const ee = new EventEmitter(); + const listener = () => console.log('Events are fun'); + ee.on('foo', listener); + getEventListeners(ee, 'foo'); // [listener] +} +{ + const et = new EventTarget(); + const listener = () => console.log('Events are fun'); + et.addEventListener('foo', listener); + getEventListeners(et, 'foo'); // [listener] +} +``` + +```cjs const { getEventListeners, EventEmitter } = require('node:events'); { @@ -897,7 +1020,32 @@ This method is intentionally generic and works with the web platform [EventTarget][WHATWG-EventTarget] interface, which has no special `'error'` event semantics and does not listen to the `'error'` event. -```js +```mjs +import { once, EventEmitter } from 'node:events'; +import process from 'node:process'; + +const ee = new EventEmitter(); + +process.nextTick(() => { + ee.emit('myevent', 42); +}); + +const [value] = await once(ee, 'myevent'); +console.log(value); + +const err = new Error('kaboom'); +process.nextTick(() => { + ee.emit('error', err); +}); + +try { + await once(ee, 'myevent'); +} catch (err) { + console.log('error happened', err); +} +``` + +```cjs const { once, EventEmitter } = require('node:events'); async function run() { @@ -930,7 +1078,21 @@ is used to wait for another event. If `events.once()` is used to wait for the '`error'` event itself, then it is treated as any other kind of event without special handling: -```js +```mjs +import { EventEmitter, once } from 'node:events'; + +const ee = new EventEmitter(); + +once(ee, 'error') + .then(([err]) => console.log('ok', err.message)) + .catch((err) => console.log('error', err.message)); + +ee.emit('error', new Error('boom')); + +// Prints: ok boom +``` + +```cjs const { EventEmitter, once } = require('node:events'); const ee = new EventEmitter(); @@ -946,7 +1108,31 @@ ee.emit('error', new Error('boom')); An {AbortSignal} can be used to cancel waiting for the event: -```js +```mjs +import { EventEmitter, once } from 'node:events'; + +const ee = new EventEmitter(); +const ac = new AbortController(); + +async function foo(emitter, event, signal) { + try { + await once(emitter, event, { signal }); + console.log('event emitted!'); + } catch (error) { + if (error.name === 'AbortError') { + console.error('Waiting for the event was canceled!'); + } else { + console.error('There was an error', error.message); + } + } +} + +foo(ee, 'foo', ac.signal); +ac.abort(); // Abort waiting for the event +ee.emit('foo'); // Prints: Waiting for the event was canceled! +``` + +```cjs const { EventEmitter, once } = require('node:events'); const ee = new EventEmitter(); @@ -979,7 +1165,31 @@ because the `process.nextTick()` queue is drained before the `Promise` microtask queue, and because `EventEmitter` emits all events synchronously, it is possible for `events.once()` to miss an event. -```js +```mjs +import { EventEmitter, once } from 'node:events'; +import process from 'node:process'; + +const myEE = new EventEmitter(); + +async function foo() { + await once(myEE, 'bar'); + console.log('bar'); + + // This Promise will never resolve because the 'foo' event will + // have already been emitted before the Promise is created. + await once(myEE, 'foo'); + console.log('foo'); +} + +process.nextTick(() => { + myEE.emit('bar'); + myEE.emit('foo'); +}); + +foo().then(() => console.log('done')); +``` + +```cjs const { EventEmitter, once } = require('node:events'); const myEE = new EventEmitter(); @@ -1006,7 +1216,26 @@ To catch both events, create each of the Promises _before_ awaiting either of them, then it becomes possible to use `Promise.all()`, `Promise.race()`, or `Promise.allSettled()`: -```js +```mjs +import { EventEmitter, once } from 'node:events'; +import process from 'node:process'; + +const myEE = new EventEmitter(); + +async function foo() { + await Promise.all([once(myEE, 'bar'), once(myEE, 'foo')]); + console.log('foo', 'bar'); +} + +process.nextTick(() => { + myEE.emit('bar'); + myEE.emit('foo'); +}); + +foo().then(() => console.log('done')); +``` + +```cjs const { EventEmitter, once } = require('node:events'); const myEE = new EventEmitter(); @@ -1075,8 +1304,19 @@ deprecated: v3.2.0 A class method that returns the number of listeners for the given `eventName` registered on the given `emitter`. -```js +```mjs +import { EventEmitter, listenerCount } from 'node:events'; + +const myEmitter = new EventEmitter(); +myEmitter.on('event', () => {}); +myEmitter.on('event', () => {}); +console.log(listenerCount(myEmitter, 'event')); +// Prints: 2 +``` + +```cjs const { EventEmitter, listenerCount } = require('node:events'); + const myEmitter = new EventEmitter(); myEmitter.on('event', () => {}); myEmitter.on('event', () => {}); @@ -1098,7 +1338,28 @@ added: * `signal` {AbortSignal} Can be used to cancel awaiting events. * Returns: {AsyncIterator} that iterates `eventName` events emitted by the `emitter` -```js +```mjs +import { on, EventEmitter } from 'node:events'; +import process from 'node:process'; + +const ee = new EventEmitter(); + +// Emit later on +process.nextTick(() => { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); +}); + +for await (const event of on(ee, 'foo')) { + // The execution of this inner block is synchronous and it + // processes one event at a time (even with await). Do not use + // if concurrent execution is required. + console.log(event); // prints ['bar'] [42] +} +// Unreachable here +``` + +```cjs const { on, EventEmitter } = require('node:events'); (async () => { @@ -1127,8 +1388,36 @@ composed of the emitted event arguments. An {AbortSignal} can be used to cancel waiting on events: -```js +```mjs +import { on, EventEmitter } from 'node:events'; +import process from 'node:process'; + +const ac = new AbortController(); + +(async () => { + const ee = new EventEmitter(); + + // Emit later on + process.nextTick(() => { + ee.emit('foo', 'bar'); + ee.emit('foo', 42); + }); + + for await (const event of on(ee, 'foo', { signal: ac.signal })) { + // The execution of this inner block is synchronous and it + // processes one event at a time (even with await). Do not use + // if concurrent execution is required. + console.log(event); // prints ['bar'] [42] + } + // Unreachable here +})(); + +process.nextTick(() => ac.abort()); +``` + +```cjs const { on, EventEmitter } = require('node:events'); + const ac = new AbortController(); (async () => { @@ -1164,7 +1453,16 @@ added: v15.4.0 or {EventEmitter} instances. If none are specified, `n` is set as the default max for all newly created {EventTarget} and {EventEmitter} objects. -```js +```mjs +import { setMaxListeners, EventEmitter } from 'node:events'; + +const target = new EventTarget(); +const emitter = new EventEmitter(); + +setMaxListeners(5, target, emitter); +``` + +```cjs const { setMaxListeners, EventEmitter @@ -1188,7 +1486,36 @@ Integrates `EventEmitter` with {AsyncResource} for `EventEmitter`s that require manual async tracking. Specifically, all events emitted by instances of `events.EventEmitterAsyncResource` will run within its [async context][]. -```js +```mjs +import { EventEmitterAsyncResource } from 'node:events'; +import { notStrictEqual, strictEqual } from 'node:assert'; +import { executionAsyncId } from 'node:async_hooks'; + +// Async tracking tooling will identify this as 'Q'. +const ee1 = new EventEmitterAsyncResource({ name: 'Q' }); + +// 'foo' listeners will run in the EventEmitters async context. +ee1.on('foo', () => { + strictEqual(executionAsyncId(), ee1.asyncId); + strictEqual(triggerAsyncId(), ee1.triggerAsyncId); +}); + +const ee2 = new EventEmitter(); + +// 'foo' listeners on ordinary EventEmitters that do not track async +// context, however, run in the same async context as the emit(). +ee2.on('foo', () => { + notStrictEqual(executionAsyncId(), ee2.asyncId); + notStrictEqual(triggerAsyncId(), ee2.triggerAsyncId); +}); + +Promise.resolve().then(() => { + ee1.emit('foo'); + ee2.emit('foo'); +}); +``` + +```cjs const { EventEmitterAsyncResource } = require('node:events'); const { notStrictEqual, strictEqual } = require('node:assert'); const { executionAsyncId } = require('node:async_hooks');