Skip to content

Commit

Permalink
added 'delayed' (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
sha1n authored Feb 25, 2022
1 parent 67bb33e commit 8c7084f
Show file tree
Hide file tree
Showing 19 changed files with 286 additions and 229 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A collection of essential time related utilities.
- [Install](#install)
- [Utilities & Features](#utilities--features)
- [delay](#delay)
- [delayed](#delayed)
- [timeoutAround](#timeoutaround)
- [timeBounded](#timebounded)
- [sleep](#sleep)
Expand All @@ -35,15 +36,22 @@ npm i @sha1n/about-time
# Utilities & Features
## delay
```ts
// Execute a function with delay and return it's value
// Executes a function with delay and returns it's value
await delay(action, { time: 10 });
await delay(action, { time: 10, units: TimeUnit.Milliseconds });
await delay(action, { time: 10, units: TimeUnit.Milliseconds, unref: true });
```

## delayed
```ts
// Returns a new function that executes the specified action with delay and returns it's value
const delayedAction = delayed(action, { time: 10, units: TimeUnit.Milliseconds, unref: true });
const result = await delayedAction();
```

## timeoutAround
```ts
// Execute a function and guards it with a specified timeout
// Executes a function and guards it with a specified timeout
await timeoutAround(action, { time: 10 });
await timeoutAround(action, { time: 10, units: TimeUnit.Milliseconds });
await timeoutAround(action, { time: 10, units: TimeUnit.Milliseconds, unref: true });
Expand All @@ -59,7 +67,7 @@ const result = await timeBoundAction();

## sleep
```ts
// Pause execution for a specified amount of time
// Pauses execution for a specified amount of time
await sleep(10);
await sleep(10, { units: TimeUnit.Seconds });
await sleep(10, { units: TimeUnit.Seconds, unref: true });
Expand All @@ -81,7 +89,7 @@ const elapsed2 = elapsed(TimeUnit.Seconds);

## until / eventually
```ts
// Wait for a condition to become true
// Waits for a condition to become true
await until(condition, { deadline: 10000 });
await until(condition, { deadline: 10000, interval: 100 });
await until(condition, { deadline: 10000, interval: 100, units: TimeUnit.Milliseconds });
Expand Down
8 changes: 6 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export { TimeUnit, toMilliseconds } from './lib/timeunit';
export { timeoutAround, timeBounded, sleep, delay, stopwatch, until, eventually } from './lib/utilities';
export * from './lib/types';
export { toMilliseconds } from './lib/toMilliseconds';
export { sleep, delay } from './lib/delay';
export { timeoutAround, timeBounded } from './lib/timeout';
export { stopwatch } from './lib/stopwatch';
export { until, eventually } from './lib/eventually';
export {
RetryPolicy,
retryAround,
Expand Down
48 changes: 48 additions & 0 deletions lib/delay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { toMilliseconds } from './toMilliseconds';
import { TimerOptions } from './types';

/**
* Zzzz...
*
* @param time the approximate time to sleep (expect it to be as accurate as setTimeout)
* @param options timer options minus the time property
* @returns a promise that resolves when the specified time has elapsed.
*/
function sleep(time: number, options?: Omit<TimerOptions, 'time'>): Promise<void> {
return new Promise(resolve => {
const timeout = setTimeout(resolve, toMilliseconds(time, options?.units));

if (options?.unref) {
timeout.unref();
}
});
}

/**
* Delays the execution of the specified action and returns its value.
*
* @param action a function to execute with delay
* @param options timer options
* @returns a promise that resolves when the specified time has elapsed.
*/
async function delay<T>(action: () => T | Promise<T>, options: TimerOptions): Promise<T> {
await sleep(options.time, options);
const result = await action();

return result;
}

/**
* Returns a new function that executes the specified action with delay.
*
* @param action a function to execute with delay
* @param options timer options
* @returns a new function.
*/
function delayed<T>(action: () => T | Promise<T>, options: TimerOptions): () => Promise<T> {
return async () => {
return delay(action, options);
};
}

export { sleep, delay, delayed };
45 changes: 45 additions & 0 deletions lib/eventually.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { toMilliseconds } from './toMilliseconds';
import { PollOptions, TimeoutError } from './types';

/**
* Awaits a specified condition to evaluate to true with or without a timeout.
*
* @param condition the condition to wait for
* @param options poll-options
* @returns a promise that resolves when the condition becomes true, or rejects when a set timeout is crossed.
*/
async function until(condition: () => boolean, options?: PollOptions): Promise<void> {
const defaultInterval = 50;
const deadline = options?.deadline ? Date.now() + toMilliseconds(options.deadline, options.units) : Number.MAX_VALUE;
const interval = options?.interval ? toMilliseconds(options.interval, options.units) : defaultInterval;

return new Promise<void>((resolve, reject) => {
const handle = setInterval(() => {
if (Date.now() > deadline) {
clearInterval(handle);
reject(new TimeoutError());
}

try {
if (condition()) {
clearInterval(handle);
resolve();
}
} catch (e) {
clearInterval(handle);
reject(e);
}
}, interval);

if (options?.unref) {
handle.unref();
}
});
}

/**
* Alias to `until`
*/
const eventually = until;

export { until, eventually };
5 changes: 3 additions & 2 deletions lib/retry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TimeUnit, toMilliseconds } from './timeunit';
import { sleep } from './utilities';
import { toMilliseconds } from './toMilliseconds';
import { sleep } from './delay';
import { TimeUnit } from './types';

interface RetryPolicy {
intervals(): Iterable<number>;
Expand Down
15 changes: 15 additions & 0 deletions lib/stopwatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { TimeUnit } from './types';

/**
* Return a function that returns the elapsed time relative to this call.
* @returns a function
*/
function stopwatch(): (units?: TimeUnit) => number {
const startTime = Date.now();

return (units?: TimeUnit) => {
return (Date.now() - startTime) / (units || TimeUnit.Milliseconds);
};
}

export { stopwatch };
50 changes: 50 additions & 0 deletions lib/timeout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { toMilliseconds } from './toMilliseconds';
import { TimerOptions, TimeoutError } from './types';

/**
* Executes an action with a specified timeout. If the action times out, rejects with TimeoutError.
*
* @param action an action to execute with timeout
* @param options timer options
* @returns the action result
*/
async function timeoutAround<T>(action: () => T | Promise<T>, options: TimerOptions): Promise<T> {
const promisedAction = new Promise<T>((resolve, reject) => {
try {
resolve(action());
} catch (e) {
reject(e);
}
});

const race = new Promise<T>((resolve, reject) => {
const timer = setTimeout(() => {
reject(new TimeoutError());
}, toMilliseconds(options.time, options.units));

if (options.unref) {
timer.unref();
}

return Promise.resolve(promisedAction).then(
r => {
clearTimeout(timer);
resolve(r);
},
err => {
clearTimeout(timer);
reject(err);
}
);
});

return race;
}

function timeBounded<T>(action: () => T | Promise<T>, options: TimerOptions): () => Promise<T> {
return () => {
return timeoutAround(action, options);
};
}

export { timeBounded, timeoutAround };
15 changes: 2 additions & 13 deletions lib/timeunit.ts → lib/toMilliseconds.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
enum TimeUnit {
Milliseconds = 1,
Millisecond = Milliseconds,
Seconds = 1000,
Second = Seconds,
Minutes = 1000 * 60,
Minute = Minutes,
Hours = 1000 * 60 * 60,
Hour = Hours,
Days = 1000 * 60 * 60 * 24,
Day = Days
}
import { TimeUnit } from './types';

/**
* Converts time value in other units to milliseconds.
Expand All @@ -22,4 +11,4 @@ function toMilliseconds(time: number, units?: TimeUnit): number {
return time * (units ? units : 1);
}

export { TimeUnit, toMilliseconds };
export { toMilliseconds };
54 changes: 54 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
enum TimeUnit {
Milliseconds = 1,
Millisecond = Milliseconds,
Seconds = 1000,
Second = Seconds,
Minutes = 1000 * 60,
Minute = Minutes,
Hours = 1000 * 60 * 60,
Hour = Hours,
Days = 1000 * 60 * 60 * 24,
Day = Days
}

class TimeoutError extends Error {
constructor(message?: string) {
super(message || 'Timeout');
}
}

type TimerOptions = {
/**
* The time to set
*/
readonly time: number;
/**
* Optional units for the specified time
*/
readonly units?: TimeUnit;
/**
* Whether to call unref on the timer
*/
readonly unref?: boolean;
};

type PollOptions = {
/**
* The poll interval to set
*/
readonly interval?: number;
/**
* The overall deadline to set
*/
readonly deadline?: number;
/**
* Time units for specified time properties
*/
readonly units?: TimeUnit;
/**
* Whether to call unref on any intervals/timers
*/
readonly unref?: boolean;
};

export { TimeUnit, PollOptions, TimeoutError, TimerOptions };
Loading

0 comments on commit 8c7084f

Please sign in to comment.