From c6558b0f0fcc5a772a3212322f5b09fc0e464c2f Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 20 Jul 2026 10:26:57 +0200 Subject: [PATCH 1/2] timers: runtime-deprecate Timeout.prototype[Symbol.dispose] Emit a DEP0208 deprecation warning when Timeout.prototype[Symbol.dispose] is called. The web platform setTimeout()/setInterval() APIs return a number, which cannot implement Symbol.dispose. Prefer clearTimeout() instead. Immediate.prototype[Symbol.dispose] is left unchanged. Fixes: https://github.com/nodejs/node/issues/58689 Signed-off-by: Matteo Collina --- doc/api/deprecations.md | 19 +++++++++++++++++++ doc/api/timers.md | 6 ++++++ lib/timers.js | 5 +++-- test/doctool/test-doc-api-json.mjs | 2 +- test/parallel/test-timers-dispose.js | 9 ++++++++- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index a9deeb118d16b8..3194c507111dc5 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -4646,6 +4646,23 @@ underlying stream are emitted from `req`. On the write-side you can use `res.writableFinished` to confirm whether the response was written successfully before the response closed. +### DEP0208: `Timeout.prototype[Symbol.dispose]()` + + + +Type: Runtime + +Calling `timeout[Symbol.dispose]()` is deprecated. The web platform +[`setTimeout()`][] and [`setInterval()`][] APIs return a number, which cannot +implement `Symbol.dispose`. Prefer [`clearTimeout()`][] instead to cancel a +timeout. This deprecation does not apply to [`Immediate`][] objects returned by +[`setImmediate()`][]. + [DEP0142]: #dep0142-repl_builtinlibs [DEP0156]: #dep0156-aborted-property-and-abort-aborted-event-in-http [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf @@ -4668,6 +4685,7 @@ successfully before the response closed. [`Decipheriv`]: crypto.md#class-decipheriv [`Duplex.toWeb()`]: stream.md#streamduplextowebstreamduplex-options [`Error.isError`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/isError +[`Immediate`]: timers.md#class-immediate [`KeyObject.from()`]: crypto.md#static-method-keyobjectfromkey [`REPLServer.clearBufferedCommand()`]: repl.md#replserverclearbufferedcommand [`ReadStream.open()`]: fs.md#class-fsreadstream @@ -4763,6 +4781,7 @@ successfully before the response closed. [`response.writableEnded`]: http.md#responsewritableended [`response.writableFinished`]: http.md#responsewritablefinished [`script.createCachedData()`]: vm.md#scriptcreatecacheddata +[`setImmediate()`]: timers.md#setimmediatecallback-args [`setInterval()`]: timers.md#setintervalcallback-delay-args [`setTimeout()`]: timers.md#settimeoutcallback-delay-args [`socket.bufferSize`]: net.md#socketbuffersize diff --git a/doc/api/timers.md b/doc/api/timers.md index 7c91543c4573f7..413e00f2a07c5d 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -177,12 +177,18 @@ thread. This allows enhanced compatibility with browser added: - v20.5.0 - v18.18.0 +deprecated: REPLACEME changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/64615 + description: Runtime deprecation. - version: v24.2.0 pr-url: https://github.com/nodejs/node/pull/58467 description: No longer experimental. --> +> Stability: 0 - Deprecated: Use [`clearTimeout()`][] instead. + Cancels the timeout. ## Scheduling timers diff --git a/lib/timers.js b/lib/timers.js index f6a2f74f5ec2c7..03a7d00c384c0d 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -52,6 +52,7 @@ const { knownTimersById, } = require('internal/timers'); const { + deprecate, promisify: { custom: customPromisify }, } = require('internal/util'); let debug = require('internal/util/debuglog').debuglog('timer', (fn) => { @@ -180,9 +181,9 @@ Timeout.prototype.close = function() { return this; }; -Timeout.prototype[SymbolDispose] = function() { +Timeout.prototype[SymbolDispose] = deprecate(function() { clearTimeout(this); -}; +}, 'Timeout.prototype[Symbol.dispose] is deprecated. Use clearTimeout instead.', 'DEP0208'); /** * Coerces a `Timeout` to a primitive. diff --git a/test/doctool/test-doc-api-json.mjs b/test/doctool/test-doc-api-json.mjs index 66302d3605c24b..e79aebca159c23 100644 --- a/test/doctool/test-doc-api-json.mjs +++ b/test/doctool/test-doc-api-json.mjs @@ -159,5 +159,5 @@ for await (const dirent of await fs.opendir(new URL('../../out/doc/api/', import assert.partialDeepStrictEqual(allExpectedKeys, findAllKeys(json)); } -assert.strictEqual(numberOfDeprecatedSections, 45); // Increase this number every time a new API is deprecated. +assert.strictEqual(numberOfDeprecatedSections, 46); // Increase this number every time a new API is deprecated. assert.strictEqual(numberOfRemovedAPIs, 46); // Increase this number every time a section is marked as removed. diff --git a/test/parallel/test-timers-dispose.js b/test/parallel/test-timers-dispose.js index a75916b4184651..826fed2db9901a 100644 --- a/test/parallel/test-timers-dispose.js +++ b/test/parallel/test-timers-dispose.js @@ -2,15 +2,22 @@ const common = require('../common'); const assert = require('assert'); +common.expectWarning({ + DeprecationWarning: { + DEP0208: 'Timeout.prototype[Symbol.dispose] is deprecated. Use clearTimeout instead.', + }, +}); + const timer = setTimeout(common.mustNotCall(), 10); const interval = setInterval(common.mustNotCall(), 10); const immediate = setImmediate(common.mustNotCall()); timer[Symbol.dispose](); +// Second call should not emit another warning (codes are warned once). interval[Symbol.dispose](); +// Immediate is not deprecated. immediate[Symbol.dispose](); - process.on('exit', () => { assert.strictEqual(timer._destroyed, true); assert.strictEqual(interval._destroyed, true); From 13422f270eed789a4862884d831291b187590e38 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 20 Jul 2026 12:21:25 +0200 Subject: [PATCH 2/2] doc: add before/after snippets for DEP0208 Add code examples showing migration from Timeout.prototype[Symbol.dispose] to clearTimeout(). Refs: https://github.com/nodejs/node/pull/64615 Signed-off-by: Matteo Collina --- doc/api/deprecations.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 3194c507111dc5..5aa1a3874d8c63 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -4663,6 +4663,31 @@ implement `Symbol.dispose`. Prefer [`clearTimeout()`][] instead to cancel a timeout. This deprecation does not apply to [`Immediate`][] objects returned by [`setImmediate()`][]. +```cjs +// Deprecated +const timeout = setTimeout(() => {}, 1000); +timeout[Symbol.dispose](); + +{ + using t = setTimeout(() => {}, 1000); +} // Calls timeout[Symbol.dispose]() +``` + +```cjs +// Use this instead +const timeout = setTimeout(() => {}, 1000); +clearTimeout(timeout); + +{ + const t = setTimeout(() => {}, 1000); + try { + // ... + } finally { + clearTimeout(t); + } +} +``` + [DEP0142]: #dep0142-repl_builtinlibs [DEP0156]: #dep0156-aborted-property-and-abort-aborted-event-in-http [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf