diff --git a/cli/js/40_testing.js b/cli/js/40_testing.js index e269b9c9f2..ec83ce370f 100644 --- a/cli/js/40_testing.js +++ b/cli/js/40_testing.js @@ -50,7 +50,7 @@ function opSanitizerDelay() { return new Promise((resolve) => { setTimeout(() => { ArrayPrototypePush(opSanitizerDelayResolveQueue, resolve); - }, 0); + }, 1); }); } diff --git a/cli/tests/unit/timers_test.ts b/cli/tests/unit/timers_test.ts index 8de7565169..c50cb779c6 100644 --- a/cli/tests/unit/timers_test.ts +++ b/cli/tests/unit/timers_test.ts @@ -557,7 +557,7 @@ Deno.test({ permissions: { run: true, read: true }, fn: async () => { const [statusCode, output] = await execCode(` - const timer = setTimeout(() => console.log("1")); + const timer = setTimeout(() => console.log("1"), 1); Deno.unrefTimer(timer); `); assertEquals(statusCode, 0); diff --git a/ext/web/02_timers.js b/ext/web/02_timers.js index cfd85a0553..ed9f1c6fb1 100644 --- a/ext/web/02_timers.js +++ b/ext/web/02_timers.js @@ -27,7 +27,10 @@ const { import * as webidl from "ext:deno_webidl/00_webidl.js"; import { reportException } from "ext:deno_web/02_event.js"; import { assert } from "ext:deno_web/00_infra.js"; -const { op_sleep } = core.generateAsyncOpHandler("op_sleep"); +const { op_sleep, op_void_async_deferred } = core.generateAsyncOpHandler( + "op_sleep", + "op_void_async_deferred", +); const hrU8 = new Uint8Array(8); const hr = new Uint32Array(TypedArrayPrototypeGetBuffer(hrU8)); @@ -218,7 +221,16 @@ const scheduledTimers = { head: null, tail: null }; */ function runAfterTimeout(cb, millis, timerInfo) { const cancelRid = timerInfo.cancelRid; - const sleepPromise = op_sleep(millis, cancelRid); + let sleepPromise; + // If this timeout is scheduled for 0ms it means we want it to run at the + // end of the event loop turn. There's no point in setting up a Tokio timer, + // since its lowest resolution is 1ms. Firing of a "void async" op is better + // in this case, because the timer will take closer to 0ms instead of >1ms. + if (millis === 0) { + sleepPromise = op_void_async_deferred(); + } else { + sleepPromise = op_sleep(millis, cancelRid); + } timerInfo.promiseId = sleepPromise[SymbolFor("Deno.core.internalPromiseId")]; if (!timerInfo.isRef) { core.unrefOp(timerInfo.promiseId); @@ -246,7 +258,8 @@ function runAfterTimeout(cb, millis, timerInfo) { PromisePrototypeThen( sleepPromise, (cancelled) => { - if (!cancelled) { + // "op_void_async_deferred" returns null + if (cancelled !== null && !cancelled) { // The timer was cancelled. removeFromScheduledTimers(timerObject); return;