Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit eb3442e

Browse files
committed
Implement scheduler.wait
1 parent 8097180 commit eb3442e

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

packages/core/src/plugins/core.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
Request,
4343
Response,
4444
ScheduledEvent,
45+
Scheduler,
4546
TextDecoder,
4647
WorkerGlobalScope,
4748
atob,
@@ -331,6 +332,7 @@ export class CorePlugin extends Plugin<CoreOptions> implements CoreOptions {
331332
clearTimeout,
332333
clearInterval,
333334
queueMicrotask,
335+
scheduler: new Scheduler(),
334336

335337
atob,
336338
btoa,

packages/core/src/standards/timers.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DOMException } from "@miniflare/core";
12
import { waitForOpenInputGate } from "@miniflare/shared";
23

34
export function inputGatedSetTimeout<Args extends any[]>(
@@ -49,3 +50,27 @@ AbortSignal.timeout ??= (ms?: number) => {
4950
setTimeout(() => controller.abort(), ms);
5051
return controller.signal;
5152
};
53+
54+
export interface SchedulerWaitOptions {
55+
signal?: AbortSignal;
56+
}
57+
58+
export class Scheduler {
59+
wait(ms?: number, options?: SchedulerWaitOptions): Promise<void> {
60+
if (typeof ms !== "number") {
61+
throw new TypeError(
62+
"Failed to execute 'wait' on 'Scheduler': parameter 1 is not of type 'integer'."
63+
);
64+
}
65+
return new Promise((resolve, reject) => {
66+
let resolved = false;
67+
const timeout = setTimeout(() => (resolved = true) && resolve(), ms);
68+
// @ts-expect-error AbortSignal in @types/node is missing EventTarget types
69+
options?.signal?.addEventListener("abort", () => {
70+
if (resolved) return;
71+
clearTimeout(timeout);
72+
reject(new DOMException("The operation was aborted", "AbortError"));
73+
});
74+
});
75+
}
76+
}

packages/core/test/plugins/core.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ test("CorePlugin: setup: includes web standards", async (t) => {
262262
t.true(typeof globals.clearTimeout === "function");
263263
t.true(typeof globals.clearInterval === "function");
264264
t.true(typeof globals.queueMicrotask === "function");
265+
t.true(typeof globals.scheduler.wait === "function");
265266

266267
t.true(typeof globals.atob === "function");
267268
t.true(typeof globals.btoa === "function");

packages/core/test/standards/timers.spec.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { setTimeout } from "timers/promises";
2-
import { inputGatedSetInterval, inputGatedSetTimeout } from "@miniflare/core";
2+
import {
3+
DOMException,
4+
Scheduler,
5+
inputGatedSetInterval,
6+
inputGatedSetTimeout,
7+
} from "@miniflare/core";
38
import {
49
TestInputGate,
510
triggerPromise,
@@ -120,3 +125,41 @@ test("AbortSignal.timeout: included on constructor obtained via AbortController#
120125
// @ts-expect-error `timeout` isn't included in Node.js yet
121126
t.is(constructor.timeout, AbortSignal.timeout);
122127
});
128+
129+
const scheduler = new Scheduler();
130+
test("scheduler.wait: resolves after timeout", async (t) => {
131+
let resolved = false;
132+
scheduler.wait(100).then(() => (resolved = true));
133+
t.false(resolved);
134+
await setTimeout(10);
135+
t.false(resolved);
136+
await setTimeout(200);
137+
t.true(resolved);
138+
});
139+
test("scheduler.wait: rejects on abort", async (t) => {
140+
const controller = new AbortController();
141+
const promise = scheduler.wait(1000, { signal: controller.signal });
142+
await setTimeout(10);
143+
controller.abort();
144+
await t.throwsAsync(promise, {
145+
instanceOf: DOMException,
146+
name: "AbortError",
147+
message: "The operation was aborted",
148+
});
149+
});
150+
test("scheduler.wait: does nothing if aborted after resolve", async (t) => {
151+
const controller = new AbortController();
152+
await scheduler.wait(10, { signal: controller.signal });
153+
controller.abort();
154+
t.pass();
155+
});
156+
test("scheduler.wait: requires numeric timeout", (t) => {
157+
const expectations: ThrowsExpectation = {
158+
instanceOf: TypeError,
159+
message:
160+
"Failed to execute 'wait' on 'Scheduler': parameter 1 is not of type 'integer'.",
161+
};
162+
t.throws(() => scheduler.wait(), expectations);
163+
// @ts-expect-error `timeout` isn't included in Node.js yet
164+
t.throws(() => scheduler.wait("42"), expectations);
165+
});

0 commit comments

Comments
 (0)