-
Notifications
You must be signed in to change notification settings - Fork 301
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add promiseWithResolvers (#5808)
Add new utility function to `@aztec/foundation` to helpt with hand-rolled promises.
- Loading branch information
Showing
5 changed files
with
81 additions
and
61 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,2 @@ | ||
export * from './running-promise.js'; | ||
export * from './utils.js'; |
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,47 @@ | ||
import { InterruptibleSleep } from '../sleep/index.js'; | ||
|
||
/** | ||
* RunningPromise is a utility class that helps manage the execution of an asynchronous function | ||
* at a specified polling interval. It allows starting, stopping, and checking the status of the | ||
* internally managed promise. The class also supports interrupting the polling process when stopped. | ||
*/ | ||
export class RunningPromise { | ||
private running = false; | ||
private runningPromise = Promise.resolve(); | ||
private interruptibleSleep = new InterruptibleSleep(); | ||
|
||
constructor(private fn: () => Promise<void>, private pollingIntervalMS = 10000) {} | ||
|
||
/** | ||
* Starts the running promise. | ||
*/ | ||
public start() { | ||
this.running = true; | ||
|
||
const poll = async () => { | ||
while (this.running) { | ||
await this.fn(); | ||
await this.interruptibleSleep.sleep(this.pollingIntervalMS); | ||
} | ||
}; | ||
this.runningPromise = poll(); | ||
} | ||
|
||
/** | ||
* Stops the running promise, resolves any pending interruptible sleep, | ||
* and waits for the currently executing function to complete. | ||
*/ | ||
async stop(): Promise<void> { | ||
this.running = false; | ||
this.interruptibleSleep.interrupt(); | ||
await this.runningPromise; | ||
} | ||
|
||
/** | ||
* Checks if the running promise is currently active. | ||
* @returns True if the promise is running. | ||
*/ | ||
public isRunning() { | ||
return this.running; | ||
} | ||
} |
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,29 @@ | ||
export type PromiseWithResolvers<T> = { | ||
promise: Promise<T>; | ||
resolve: (value: T) => void; | ||
reject: (reason?: any) => void; | ||
}; | ||
|
||
/** | ||
* A polyfill for the Promise.withResolvers proposed API. | ||
* @see https://github.com/tc39/proposal-promise-with-resolvers | ||
* @returns A promise with resolvers. | ||
*/ | ||
export function promiseWithResolvers<T>(): PromiseWithResolvers<T> { | ||
// use ! operator to avoid TS error | ||
let resolve!: (value: T) => void; | ||
let reject!: (reason?: any) => void; | ||
|
||
// the ES spec guarantees that the promise executor is called synchronously | ||
// so the resolve and reject functions will be defined | ||
const promise = new Promise<T>((res, rej) => { | ||
resolve = res; | ||
reject = rej; | ||
}); | ||
|
||
return { | ||
promise, | ||
resolve, | ||
reject, | ||
}; | ||
} |
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 |
---|---|---|
@@ -1,60 +1 @@ | ||
/** | ||
* RunningPromise is a utility class that helps manage the execution of an asynchronous function | ||
* at a specified polling interval. It allows starting, stopping, and checking the status of the | ||
* internally managed promise. The class also supports interrupting the polling process when stopped. | ||
*/ | ||
export class RunningPromise { | ||
private running = false; | ||
private runningPromise = Promise.resolve(); | ||
private interruptPromise = Promise.resolve(); | ||
private interruptResolve = () => {}; | ||
constructor(private fn: () => Promise<void>, private pollingInterval = 10000) {} | ||
|
||
/** | ||
* Starts the running promise. | ||
*/ | ||
public start() { | ||
this.running = true; | ||
this.interruptPromise = new Promise(resolve => (this.interruptResolve = resolve)); | ||
|
||
const poll = async () => { | ||
while (this.running) { | ||
await this.fn(); | ||
await this.interruptibleSleep(this.pollingInterval); | ||
} | ||
}; | ||
this.runningPromise = poll(); | ||
} | ||
|
||
/** | ||
* Stops the running promise, resolves any pending interruptible sleep, | ||
* and waits for the currently executing function to complete. | ||
*/ | ||
async stop(): Promise<void> { | ||
this.running = false; | ||
this.interruptResolve(); | ||
await this.runningPromise; | ||
} | ||
|
||
/** | ||
* A sleep function that can be interrupted before the specified time. | ||
* The sleep duration is determined by 'timeInMs', and it can be terminated early if the 'interruptPromise' is resolved. | ||
* @param timeInMs - The time in milliseconds. | ||
*/ | ||
private async interruptibleSleep(timeInMs: number) { | ||
let timeout!: NodeJS.Timeout; | ||
const sleepPromise = new Promise(resolve => { | ||
timeout = setTimeout(resolve, timeInMs); | ||
}); | ||
await Promise.race([sleepPromise, this.interruptPromise]); | ||
clearTimeout(timeout); | ||
} | ||
|
||
/** | ||
* Checks if the running promise is currently active. | ||
* @returns True if the promise is running. | ||
*/ | ||
public isRunning() { | ||
return this.running; | ||
} | ||
} | ||
export * from '../promise/running-promise.js'; |