Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

events: addAbortListener ignores disposal and passes undefined event when signal is already aborted

Offen
#65,637 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
75/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
javascript
Bereich
backend

Rechercherichtung

Beginne damit, das bereitgestellte Skript zu reproduzieren, und lies dann lib/internal/events/abort_listener.js, wobei du dich auf den signal.aborted-Zweig und die Behandlung von SymbolDispose konzentrierst. Füge Regressionstests für die Freigabe vor dem eingereihten Microtask und für das Callback-Argument hinzu. Überprüfe anschließend, dass der Listener nach der Freigabe unterdrückt wird und andernfalls ein Abort-Event erhält.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Version

Tested on v24.7.0, reproduces on main (commit a48e33fb6f).

Platform
All platforms (pure JS logic in `lib/internal/events/abort_listener.js`).
Subsystem

events

What steps will reproduce the bug?
'use strict';
const { addAbortListener } = require('node:events');

// --- Bug 1: Disposing the listener does not cancel it if the signal was already aborted ---
const ac = new AbortController();
ac.abort();

let listenerCalledAfterDispose = false;
{
  using _ = addAbortListener(ac.signal, (event) => {
    listenerCalledAfterDispose = true;
    console.log('Event argument received:', event); // Bug 2: logs 'undefined' instead of an Event object
  });
  // Scope exits here -> disposable[Symbol.dispose]() is invoked synchronously.
}

queueMicrotask(() => {
  console.log('Listener called after disposal:', listenerCalledAfterDispose);
  // Prints: true (Expected: false)
});
How often does it reproduce? Is there a required condition?

100% reproducible whenever addAbortListener() is called on an AbortSignal where signal.aborted === true.

What is the expected behavior? Why is that the expected behavior?
  1. Calling disposable[Symbol.dispose]() (or exiting a using scope) before the microtask executes should disarm/cancel the scheduled listener. The main motivation for addAbortListener() is TC39 explicit resource management, so disposal should always prevent the callback from firing.
  2. The listener callback should receive an Event object (type === 'abort'), consistent with when the listener is triggered by an active signal.
What do you see instead?
  1. The listener runs unconditionally in the microtask, ignoring Symbol.dispose().
  2. The listener receives undefined instead of an Event instance. Code following the documented (e) => { ... } signature that accesses properties like e.type crashes with a TypeError inside the microtask.
Additional information

In lib/internal/events/abort_listener.js:

  let removeEventListener;
  if (signal.aborted) {
    queueMicrotask ??= require('internal/process/task_queues').queueMicrotask;
    queueMicrotask(() => listener()); // <-- 1. Listener called with no argument
  } else {
    kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
    abortListenerOptions ??= ObjectFreeze({ __proto__: null, once: true, [kResistStopPropagation]: true });
    signal.addEventListener('abort', listener, abortListenerOptions);
    removeEventListener = () => {
      signal.removeEventListener('abort', listener);
    };
  }
  return {
    __proto__: null,
    [SymbolDispose]() {
      removeEventListener?.(); // <-- 2. removeEventListener is undefined on the aborted branch
    },
  };

When signal.aborted is true:

  • removeEventListener is left undefined, making SymbolDispose a no-op.
  • queueMicrotask(() => listener()) invokes listener without passing an Event object.

A possible fix is tracking a disposed flag for the microtask and creating an Event('abort') instance:

  let removeEventListener;
  if (signal.aborted) {
    queueMicrotask ??= require('internal/process/task_queues').queueMicrotask;
    let disposed = false;
    queueMicrotask(() => {
      if (!disposed) {
        const { Event } = require('internal/event_target');
        listener(new Event('abort', { cancelable: false, bubbles: false }));
      }
    });
    removeEventListener = () => {
      disposed = true;
    };
  } else {
    // ...
Vorherrschende Sprache
JavaScript
Sterne
122k
Forks
37.4k
Ø Merge
4 T. 3 Std.
Gemergte PRs (30 T.)
279

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus nodejs/node

Alle Issues in nodejs/node

Ähnliche Issues

Weitere Issues zu JavaScript

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.