Skip to content

Commit 1830f2a

Browse files
committed
events: match dispatch state on synthesized abort event
Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com>
1 parent 82a3438 commit 1830f2a

4 files changed

Lines changed: 21 additions & 15 deletions

File tree

‎doc/api/events.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,6 +1883,10 @@ added:
18831883
- v20.5.0
18841884
- v18.18.0
18851885
changes:
1886+
- version: REPLACEME
1887+
pr-url: https://github.com/nodejs/node/pull/65640
1888+
description: When the signal is already aborted, the listener now receives an
1889+
`abort` event and disposing cancels the pending call.
18861890
- version:
18871891
- v24.0.0
18881892
- v22.16.0
@@ -1908,6 +1912,9 @@ not prevent the listener from running.
19081912

19091913
Returns a disposable so that it may be unsubscribed from more easily.
19101914

1915+
If `signal` is already aborted, the listener is called with an `abort` event in a
1916+
microtask. Disposing before that microtask runs cancels the call.
1917+
19111918
```cjs
19121919
const { addAbortListener } = require('node:events');
19131920

‎lib/internal/event_target.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ module.exports = {
12431243
kTrustEvent,
12441244
kRemoveListener,
12451245
kEvents,
1246+
kIsBeingDispatched,
12461247
kTarget,
12471248
kWeakHandler,
12481249
kResistStopPropagation,

‎lib/internal/events/abort_listener.js‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ const {
1717
let queueMicrotask;
1818
let kResistStopPropagation;
1919
let abortListenerOptions;
20-
let Event;
21-
let kTarget;
22-
let kTrustEvent;
20+
let eventTarget;
2321

2422
/**
2523
* @param {AbortSignal} signal
@@ -36,15 +34,16 @@ function addAbortListener(signal, listener) {
3634
let removeEventListener;
3735
if (signal.aborted) {
3836
queueMicrotask ??= require('internal/process/task_queues').queueMicrotask;
39-
if (Event === undefined) {
40-
({ Event, kTarget, kTrustEvent } = require('internal/event_target'));
41-
}
37+
eventTarget ??= require('internal/event_target');
4238
let disposed = false;
4339
queueMicrotask(() => {
4440
if (disposed) return;
41+
const { Event, kIsBeingDispatched, kTarget, kTrustEvent } = eventTarget;
4542
const event = new Event('abort', { [kTrustEvent]: true });
4643
event[kTarget] = signal;
44+
event[kIsBeingDispatched] = true;
4745
listener(event);
46+
event[kIsBeingDispatched] = false;
4847
});
4948
removeEventListener = () => {
5049
disposed = true;

‎test/parallel/test-events-add-abort-listener.mjs‎

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,21 @@ describe('events.addAbortListener', () => {
3535

3636
it('should pass an abort event to the listener for aborted runners', async () => {
3737
const signal = AbortSignal.abort();
38-
events.addAbortListener(
39-
signal,
40-
common.mustCall((e) => {
41-
assert.strictEqual(e.type, 'abort');
42-
assert.strictEqual(e.target, signal);
43-
}),
44-
);
38+
const { promise, resolve } = Promise.withResolvers();
39+
events.addAbortListener(signal, (e) => resolve([e, e?.currentTarget]));
4540

46-
await new Promise(queueMicrotask);
41+
const [event, currentTarget] = await promise;
42+
assert.strictEqual(event.type, 'abort');
43+
assert.strictEqual(event.target, signal);
44+
assert.strictEqual(event.isTrusted, true);
45+
assert.strictEqual(currentTarget, signal);
4746
});
4847

4948
it('should not execute the listener for aborted runners when disposed', async () => {
5049
const disposable = events.addAbortListener(AbortSignal.abort(), common.mustNotCall());
5150
disposable[Symbol.dispose]();
5251

53-
await new Promise(queueMicrotask);
52+
await new Promise(setImmediate);
5453
});
5554

5655
it('should execute the listener even when event propagation stopped', () => {

0 commit comments

Comments
 (0)