43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
|
// Based on https://github.com/jsoendermann/semaphore-async-await/blob/master/src/Semaphore.ts
|
||
|
export class Lock {
|
||
|
private permits: number = 1
|
||
|
private promiseResolverQueue: Array<(v: boolean) => void> = []
|
||
|
|
||
|
/**
|
||
|
* Returns a promise used to wait for a permit to become available. This method should be awaited on.
|
||
|
* @returns A promise that gets resolved when execution is allowed to proceed.
|
||
|
*/
|
||
|
public async acquire(): Promise<boolean> {
|
||
|
if (this.permits > 0) {
|
||
|
this.permits -= 1
|
||
|
return Promise.resolve(true)
|
||
|
}
|
||
|
|
||
|
// If there is no permit available, we return a promise that resolves once the semaphore gets
|
||
|
// signaled enough times that permits is equal to one.
|
||
|
return new Promise<boolean>((resolver) => this.promiseResolverQueue.push(resolver))
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Increases the number of permits by one. If there are other functions waiting, one of them will
|
||
|
* continue to execute in a future iteration of the event loop.
|
||
|
*/
|
||
|
public release(): void {
|
||
|
this.permits += 1
|
||
|
|
||
|
if (this.permits > 1 && this.promiseResolverQueue.length > 0) {
|
||
|
// eslint-disable-next-line no-console
|
||
|
console.warn('Lock.permits should never be > 0 when there is someone waiting.')
|
||
|
} else if (this.permits === 1 && this.promiseResolverQueue.length > 0) {
|
||
|
// If there is someone else waiting, immediately consume the permit that was released
|
||
|
// at the beginning of this function and let the waiting function resume.
|
||
|
this.permits -= 1
|
||
|
|
||
|
const nextResolver = this.promiseResolverQueue.shift()
|
||
|
if (nextResolver) {
|
||
|
nextResolver(true)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|