events: addAbortListener ignores disposal and passes undefined event when signal is already aborted
還沒有人認領這個 Issue。
評估
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 新手友好度
- 75/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 活躍
- 技術堆疊
- javascript
- 領域
- backend
研究方向
先重現提供的指令碼,然後閱讀 lib/internal/events/abort_listener.js,專注於 signal.aborted 分支和 SymbolDispose 處理。為排隊的 microtask 之前進行 disposal 以及 callback 引數新增回歸測試,接著驗證 disposal 後 listener 會被抑制,否則會接收到 abort Event。
由索引模型根據 Issue 內容生成。
描述
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?
- Calling
disposable[Symbol.dispose]()(or exiting ausingscope) before the microtask executes should disarm/cancel the scheduled listener. The main motivation foraddAbortListener()is TC39 explicit resource management, so disposal should always prevent the callback from firing. - The listener callback should receive an
Eventobject (type === 'abort'), consistent with when the listener is triggered by an active signal.
What do you see instead?
- The listener runs unconditionally in the microtask, ignoring
Symbol.dispose(). - The listener receives
undefinedinstead of anEventinstance. Code following the documented(e) => { ... }signature that accesses properties likee.typecrashes with aTypeErrorinside 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:
removeEventListeneris leftundefined, makingSymbolDisposea no-op.queueMicrotask(() => listener())invokeslistenerwithout passing anEventobject.
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 {
// ...
- 主要語言
- JavaScript
- 星號
- 122k
- 分支
- 37.4k
- 平均合併
- 4 天 3 小時
- 30 天內合併 PR
- 279
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
nodejs/node 的其他 Issue
-
doc
難度 2/5 1-3 小時 新手友好度 65/100
-
build
難度 1/5 1 小時以內 新手友好度 88/100
-
難度 2/5 1-3 小時 新手友好度 84/100
-
難度 1/5 1 小時以內 新手友好度 90/100
-
feature request
難度 2/5 1-3 小時 新手友好度 68/100
相似的 Issue
-
bug confirmed issue
難度 2/5 1-3 小時 新手友好度 75/100
open-webui/open-webui#30750 · 1 則留言 ·
-
難度 2/5 1-3 小時 新手友好度 75/100
-
難度 2/5 1-3 小時 新手友好度 70/100
-
難度 2/5 1-3 小時 新手友好度 75/100
-
Mend: dependency security vulnerability untriaged
難度 2/5 1-3 小時 新手友好度 70/100