-
Notifications
You must be signed in to change notification settings - Fork 330
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
455 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,320 @@ | ||
import { validateFunction, validateNumber } from 'node-internal:validators'; | ||
import { ERR_OUT_OF_RANGE } from 'node-internal:internal_errors'; | ||
|
||
// Timeout values > TIMEOUT_MAX are set to 1. | ||
const TIMEOUT_MAX = 2 ** 31 - 1; | ||
|
||
let clearTimeoutImpl: (obj: Timeout) => void; | ||
let clearImmediateImpl: (obj: Immediate) => void; | ||
|
||
function getTimerDuration(msecs: unknown, name: string): number { | ||
validateNumber(msecs, name); | ||
if (msecs < 0 || !Number.isFinite(msecs)) { | ||
throw new ERR_OUT_OF_RANGE(name, 'a non-negative finite number', msecs); | ||
} | ||
|
||
// Ensure that msecs fits into signed int32 | ||
if (msecs > TIMEOUT_MAX) { | ||
return TIMEOUT_MAX; | ||
} | ||
|
||
return msecs; | ||
} | ||
|
||
class Timeout { | ||
#timer: string | number | this | undefined; | ||
#callback: (...args: unknown[]) => unknown; | ||
#after: number; | ||
#args: unknown[]; | ||
#isRepeat: boolean; | ||
#isRefed: boolean; | ||
|
||
public constructor( | ||
callback: (...args: unknown[]) => unknown, | ||
after?: number, | ||
args: unknown[] = [], | ||
isRepeat: boolean = false, | ||
isRefed: boolean = false | ||
) { | ||
if (after === undefined) { | ||
after = 1; | ||
} else { | ||
after *= 1; // Coalesce to number or NaN | ||
} | ||
|
||
this.#callback = callback; | ||
this.#after = after; | ||
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); | ||
} | ||
{ | ||
globalThis.clearTimeout(this.#timer); | ||
} | ||
} | ||
|
||
public refresh(): this { | ||
this.#clearTimeout(); | ||
this.#constructTimer(); | ||
return this; | ||
} | ||
|
||
public unref(): this { | ||
// TODO(soon): Implement this | ||
this.#isRefed = false; | ||
return this; | ||
} | ||
|
||
public ref(): this { | ||
// TODO(soon): Implement this | ||
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 { | ||
// @ts-expect-error TS2322 Timer is actually an ID. | ||
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 { | ||
// TODO: Implement this | ||
this.#hasRef = true; | ||
return this; | ||
} | ||
|
||
public unref(): this { | ||
// TODO: Implement this | ||
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, | ||
arg1?: unknown, | ||
arg2?: unknown, | ||
arg3?: unknown | ||
): Timeout { | ||
validateFunction(callback, 'callback'); | ||
|
||
let i, args; | ||
switch (arguments.length) { | ||
// fast cases | ||
case 1: | ||
case 2: | ||
break; | ||
case 3: | ||
args = [arg1]; | ||
break; | ||
case 4: | ||
args = [arg1, arg2]; | ||
break; | ||
default: | ||
args = [arg1, arg2, arg3]; | ||
for (i = 5; i < arguments.length; i++) { | ||
// eslint-disable-next-line prefer-rest-params | ||
args.push(arguments[i]); | ||
} | ||
break; | ||
} | ||
|
||
return new Timeout( | ||
callback, | ||
after, | ||
args, | ||
/* isRepeat */ false, | ||
/* isRefed */ true | ||
); | ||
} | ||
|
||
export function clearTimeout(timer: unknown): void { | ||
if (timer instanceof Timeout) { | ||
clearTimeoutImpl(timer); | ||
return; | ||
} else if (typeof timer === 'number') { | ||
globalThis.clearTimeout(timer); | ||
} | ||
} | ||
|
||
export function setImmediate( | ||
callback: (...args: unknown[]) => void, | ||
arg1?: unknown, | ||
arg2?: unknown, | ||
arg3?: unknown | ||
): Immediate { | ||
validateFunction(callback, 'callback'); | ||
|
||
let i, args; | ||
switch (arguments.length) { | ||
// fast cases | ||
case 1: | ||
break; | ||
case 2: | ||
args = [arg1]; | ||
break; | ||
case 3: | ||
args = [arg1, arg2]; | ||
break; | ||
default: | ||
args = [arg1, arg2, arg3]; | ||
for (i = 4; i < arguments.length; i++) { | ||
// eslint-disable-next-line prefer-rest-params | ||
args.push(arguments[i]); | ||
} | ||
break; | ||
} | ||
|
||
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, | ||
arg1?: unknown, | ||
arg2?: unknown, | ||
arg3?: unknown | ||
): Timeout { | ||
validateFunction(callback, 'callback'); | ||
|
||
let i, args; | ||
switch (arguments.length) { | ||
// fast cases | ||
case 1: | ||
case 2: | ||
break; | ||
case 3: | ||
args = [arg1]; | ||
break; | ||
case 4: | ||
args = [arg1, arg2]; | ||
break; | ||
default: | ||
args = [arg1, arg2, arg3]; | ||
for (i = 5; i < arguments.length; i++) { | ||
// eslint-disable-next-line prefer-rest-params | ||
args.push(arguments[i]); | ||
} | ||
break; | ||
} | ||
|
||
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); | ||
} | ||
} | ||
|
||
/** | ||
* @deprecated Please use setTimeout instead. | ||
*/ | ||
export function enroll(_item: unknown, msecs: number): void { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
msecs = getTimerDuration(msecs, 'msecs'); | ||
// TODO(soon): Implement this. | ||
throw new Error('Not implemented'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* 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); | ||
} |
Oops, something went wrong.