Repository navigation
Description
Test I ran
node timer_handles.ts
slice run timer_handles.tstimer_handles.ts
const show = async (k: string, f: () => unknown) => { let v; try { v = await Promise.race([Promise.resolve().then(f), new Promise((r) => setTimeout(() => r("NEVER SETTLED (2000 ms)"), 2000))]); } catch (e: any) { v = "THROWS " + e.name + ": " + String(e.message).slice(0, 100); } console.log(k.padEnd(52) + JSON.stringify(v)); };
(async () => {
const h: any = setTimeout(() => {}, 10);
await show("typeof setTimeout(...)", () => typeof h);
await show("typeof .unref/.ref/.hasRef/.refresh", () => [typeof h.unref, typeof h.ref, typeof h.hasRef, typeof h.refresh]);
await show("setTimeout(() => {}, 10).unref()", () => { h.unref(); return "ok"; });
await show("setInterval(() => {}, 1000).unref()", () => { const i: any = setInterval(() => {}, 1000); try { i.unref(); return "ok"; } finally { clearInterval(i); } });
clearTimeout(h);
})();Result
Node v24.22.2:
typeof setTimeout(...) "object"
typeof .unref/.ref/.hasRef/.refresh ["function","function","function","function"]
setTimeout(() => {}, 10).unref() "ok"
setInterval(() => {}, 1000).unref() "ok"
Slice v0.10.7:
typeof setTimeout(...) "number"
typeof .unref/.ref/.hasRef/.refresh ["undefined","undefined","undefined","undefined"]
setTimeout(() => {}, 10).unref() "THROWS TypeError: not a callable function"
setInterval(() => {}, 1000).unref() "THROWS TypeError: not a callable function"
What is wrong
setTimeout / setInterval return a number, so the Node Timeout methods do not exist and calling them throws TypeError: not a callable function.
A numeric id is what browsers and WinterTC specify, so this is a Node-compatibility gap, not a spec violation. It matters because setTimeout(fn, ms).unref() and setInterval(fn, ms).unref() are common in npm packages (keep-alive pings, cache sweepers, connection pools, metrics flushers), and there the call is unconditional, so the package throws on load or on first use.
A small wrapper object with no-op ref() / unref() (returning this), hasRef(), refresh() and [Symbol.toPrimitive]() returning the id would keep clearTimeout(handle) working and let those packages run. node:timers is listed as supported in COMPATIBILITY.md; the handle methods are not mentioned either way.
Environment
- Slice
v0.10.7, commitea7c70c, built withcargo +1.96.0 build --release --locked --bin slice - Reference: Node.js
v24.22.2(same script, same machine) - Ubuntu 26.04, Linux 7.0, x86_64
- The same output is produced by the
v0.10.5tag, so this is not a regression in 0.10.6/0.10.7.
Activity
siktec-lab commentedon Nov 11, 2026
Addressed in #67 (�0.10.11): updated setTimeout, setInterval, and setImmediate to return Node-compatible Timeout / Immediate handle objects ( ypeof h === 'object') with .unref(), .ref(), .hasRef(), .refresh(), Symbol.toPrimitive, and �alueOf(), and updated the event loop so unref'd timers do not keep the isolate alive when no ref'd work remains.