-
Notifications
You must be signed in to change notification settings - Fork 0
/
PromiseChain.ts
72 lines (63 loc) · 2.79 KB
/
PromiseChain.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* Copyright 2021 The @ink-feather-org/ts-utils Contributors.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */
import { GeneralAsyncFunction, PromiseRejectFunction, PromiseResolveFunction } from './tsutils'
/**
* @internal
*/
class QueuedRequest<T> {
constructor(private readonly request: GeneralAsyncFunction<never, T>,
private readonly resolve: PromiseResolveFunction<T>,
private readonly reject: PromiseRejectFunction) {}
async execute() {
try {
this.resolve(await this.request())
} catch (err) {
this.reject(err)
}
}
}
/**
* This class allows async functions to be executed in the order they were enqueued.
* It guarantees that no functions execute concurrently and that they are executed in order.
*/
export class PromiseChain {
private queuedRequests = new Array<QueuedRequest<unknown>>()
private handlingRequests = false
private async startHandlingRequests(): Promise<void> {
this.handlingRequests = true
while (this.queuedRequests.length) {
const nextReq = this.queuedRequests.shift()!
await nextReq.execute()
}
this.handlingRequests = false
}
/**
* Enqueues the specified async function for execution.
* It will be added to an internal queue and executed once all prior functions have finished.
* @param asyncFunction The async function to execute.
* @return A promise that is fulfilled once the asyncFunction has finished its execution. It will contain the return value of the supplied function.
*/
async enqueue<T>(asyncFunction: GeneralAsyncFunction<never, T>): Promise<T> {
try {
return await new Promise<T>((resolve, reject) => {
this.queuedRequests.push(new QueuedRequest(asyncFunction, resolve, reject) as QueuedRequest<unknown>)
if (!this.handlingRequests)
this.startHandlingRequests()
})
} catch (err) {
// improve stack traces for chrome
if (Object.getOwnPropertyDescriptor(err, 'stack')?.configurable) {
const originalStack = (err as Error).stack
const newEx = Error()
Object.defineProperty(err, 'stack', {
get() {
return `Inside PromiseChain:\n${originalStack}\nOutside PromiseChain:\n${newEx.stack}`
},
})
}
throw err
}
}
}