Skip to content

Commit

Permalink
add node:timers module
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Jan 16, 2025
1 parent a33c6d2 commit 23b0804
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/node/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ wd_ts_bundle(
"stream/*.js",
"path/*.ts",
"util/*.ts",
"timers/*.ts",
]),
schema_id = "0xbcc8f57c63814005",
tsconfig_json = "tsconfig.json",
Expand Down
250 changes: 250 additions & 0 deletions src/node/internal/internal_timers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
// Copyright (c) 2017-2022 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0
//
// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

import { validateFunction } from 'node-internal:validators';

let clearTimeoutImpl: (obj: Timeout) => void;
let clearImmediateImpl: (obj: Immediate) => void;

class Timeout {
// @ts-expect-error TS2564 It has a default value, but TS fails to detect it.
#timer: number;
#callback: (...args: unknown[]) => unknown;
#after: number;
#args: unknown[];
#isRepeat: boolean;
#isRefed: boolean;

public constructor(
callback: (...args: unknown[]) => unknown,
after: number = 1,
args: unknown[] = [],
isRepeat: boolean = false,
isRefed: boolean = false
) {
this.#callback = callback;
// Left it as multiply by 1 due to make the behavior as similar to Node.js
// as possible.
this.#after = after * 1;
this.#args = args;
this.#isRepeat = isRepeat;
this.#isRefed = isRefed;
this.#constructTimer();
}

#constructTimer(): void {
if (this.#isRepeat) {
// @ts-expect-error TS2322 Due to difference between Node.js and globals
this.#timer = globalThis.setInterval(
() => this.#callback(...this.#args),
this.#after
);
} else {
// @ts-expect-error TS2322 Due to difference between Node.js and globals
this.#timer = globalThis.setTimeout(
() => this.#callback(...this.#args),
this.#after
);
}
}

#clearTimeout(): void {
if (this.#isRepeat) {
globalThis.clearInterval(this.#timer);
} else {
globalThis.clearTimeout(this.#timer);
}
}

public refresh(): this {
this.#clearTimeout();
this.#constructTimer();
return this;
}

public unref(): this {
// Intentionally left as no-op.
this.#isRefed = false;
return this;
}

public ref(): this {
// Intentionally left as no-op.
this.#isRefed = true;
return this;
}

public hasRef(): boolean {
return this.#isRefed;
}

public close(): this {
this.#clearTimeout();
return this;
}

public [Symbol.dispose](): void {
this.#clearTimeout();
}

public [Symbol.toPrimitive](): number {
return this.#timer;
}

static {
clearTimeoutImpl = (obj: Timeout): void => {
obj.#clearTimeout();
};
}
}

class Immediate {
// @ts-expect-error TS2724 Node.js and global difference.
#timer: globalThis.Immediate;
#hasRef: boolean = false;

public constructor(
callback: (...args: unknown[]) => void,
args: unknown[] = []
) {
this.#timer = globalThis.setImmediate(callback, ...args);
}

public ref(): this {
// Intentionally left as no-op.
this.#hasRef = true;
return this;
}

public unref(): this {
// Intentionally left as no-op.
this.#hasRef = false;
return this;
}

public hasRef(): boolean {
return this.#hasRef;
}

public [Symbol.dispose](): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
globalThis.clearImmediate(this.#timer);
}

static {
clearImmediateImpl = (obj: Immediate): void => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
globalThis.clearImmediate(obj.#timer);
};
}
}

export function setTimeout(
callback: (...args: unknown[]) => unknown,
after: number,
...args: unknown[]
): Timeout {
validateFunction(callback, 'callback');

return new Timeout(
callback,
after,
args,
/* isRepeat */ false,
/* isRefed */ true
);
}

export function clearTimeout(timer: unknown): void {
if (timer instanceof Timeout) {
clearTimeoutImpl(timer);
} else if (typeof timer === 'number') {
globalThis.clearTimeout(timer);
}
}

export function setImmediate(
callback: (...args: unknown[]) => void,
...args: unknown[]
): Immediate {
validateFunction(callback, 'callback');
return new Immediate(callback, args);
}

export function clearImmediate(immediate?: Immediate): void {
if (immediate != null) {
clearImmediateImpl(immediate);
}
}

export function setInterval(
callback: (...args: unknown[]) => void,
repeat: number,
...args: unknown[]
): Timeout {
validateFunction(callback, 'callback');
return new Timeout(
callback,
repeat,
args,
/* isRepeat */ true,
/* isRefed */ true
);
}

export function clearInterval(timer: unknown): void {
if (timer instanceof Timeout) {
clearTimeoutImpl(timer);
} else if (typeof timer === 'number') {
globalThis.clearInterval(timer);
}
}

/**
* @deprecated Please use timeout.refresh() instead.
*/
export function active(timer: unknown): void {
if (timer instanceof Timeout) {
timer.refresh();
}
}

/**
* @deprecated Please use clearTimeout instead.
*/
export function unenroll(timer: unknown): void {
if (timer instanceof Timeout) {
clearTimeoutImpl(timer);
} else if (typeof timer === 'number') {
globalThis.clearTimeout(timer);
}
}

/**
* @deprecated Please use setTimeout instead.
*/
export function enroll(_item: unknown, _msecs: number): void {
throw new Error('Not implemented. Please use setTimeout() instead.');
}
82 changes: 82 additions & 0 deletions src/node/internal/internal_timers_promises.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) 2017-2022 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0
//
// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

/* eslint-disable @typescript-eslint/require-await,@typescript-eslint/no-deprecated,prefer-spread */

import * as timers from 'node-internal:internal_timers';

export async function setTimeout(
...args: Parameters<typeof timers.setTimeout>
): Promise<ReturnType<typeof timers.setTimeout>> {
return timers.setTimeout.apply(timers, args);
}

export async function clearTimeout(
...args: Parameters<typeof timers.clearTimeout>
): Promise<void> {
timers.clearTimeout.apply(timers, args);
}

export async function setImmediate(
...args: Parameters<typeof timers.setImmediate>
): Promise<ReturnType<typeof timers.setImmediate>> {
return timers.setImmediate.apply(timers, args);
}

export async function clearImmediate(
...args: Parameters<typeof timers.clearImmediate>
): Promise<void> {
timers.clearImmediate.apply(timers, args);
}

export async function setInterval(
...args: Parameters<typeof timers.setInterval>
): Promise<ReturnType<typeof timers.setInterval>> {
return timers.setInterval.apply(timers, args);
}

export async function clearInterval(
...args: Parameters<typeof timers.clearInterval>
): Promise<void> {
timers.clearInterval.apply(timers, args);
}

export async function active(
...args: Parameters<typeof timers.active>
): Promise<ReturnType<typeof timers.active>> {
timers.active.apply(timers, args);
}

export async function unenroll(
...args: Parameters<typeof timers.unenroll>
): Promise<void> {
timers.unenroll.apply(timers, args);
}

export async function enroll(
...args: Parameters<typeof timers.enroll>
): Promise<void> {
timers.enroll.apply(timers, args);
}
28 changes: 28 additions & 0 deletions src/node/timers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as _promises from 'node-internal:internal_timers_promises';
import {
setTimeout,
clearTimeout,
setImmediate,
clearImmediate,
setInterval,
clearInterval,
active,
unenroll,
enroll,
} from 'node-internal:internal_timers';

export * from 'node-internal:internal_timers';
export const promises = _promises;

export default {
promises: _promises,
setTimeout,
clearTimeout,
setImmediate,
clearImmediate,
setInterval,
clearInterval,
active,
unenroll,
enroll,
};
28 changes: 28 additions & 0 deletions src/node/timers/promises.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2017-2022 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0
//
// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

import * as _default from 'node-internal:internal_timers_promises';
export * from 'node-internal:internal_timers_promises';
export default _default;
Loading

0 comments on commit 23b0804

Please sign in to comment.