chain_graphql/src/utils/promise.util.ts
CounterFire2023 ded16ac57b project init
2023-06-25 11:11:49 +08:00

139 lines
3.2 KiB
TypeScript

type RetryOptions = {
maxRetries: number
whitelistErrors: Error[]
}
/**
* 使用:
* retry(() => fetch("https://example.com"), { maxRetries: 3, whitelistErrors: [] })
* .then((response) => console.log(response))
* .catch((error) => console.error(error));
* @param promiseFn
* @param options
* @returns
*/
export function retry<T>(promiseFn: () => Promise<T>, options: RetryOptions): Promise<T> {
let retries = 0
let defaultOptions = {
maxRetries: 3,
whitelistErrors: [],
}
Object.assign(defaultOptions, options)
const { maxRetries, whitelistErrors } = options
const retryPromise = async (): Promise<T> => {
try {
return await promiseFn()
} catch (err) {
if (
retries < maxRetries &&
whitelistErrors.some(whitelistedError => err instanceof whitelistedError.constructor)
) {
retries++
return retryPromise()
}
throw err
}
}
return retryPromise()
}
/**
* 构建一个promise, 在
* usage:
* function delay(ms: number): Promise<void> {
const deferred = new Deferred<void>();
setTimeout(() => {
deferred.resolve();
}, ms);
return deferred.promise;
}
console.log("start");
delay(1000).then(() => {
console.log("after 1 second");
});
console.log("end");
*/
export class Deferred<T = any> {
private _resolve!: (value: T | PromiseLike<T>) => void
private _reject!: (reason?: any) => void
public readonly promise: Promise<T>
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve
this._reject = reject
})
}
public resolve(value: T | PromiseLike<T>): void {
this._resolve(value)
}
public reject(reason?: any): void {
this._reject(reason)
}
public then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined,
): Promise<TResult1 | TResult2> {
return this.promise.then(onfulfilled, onrejected)
}
public catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined,
): Promise<T | TResult> {
return this.promise.catch(onrejected)
}
}
/**
* 简单限流的 Promise 队列
* usage:
const q = new PromiseQueue();
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].forEach((v) => {
q.add(
() =>
new Promise((resolve) => {
setTimeout(() => {
console.log(v);
resolve();
}, 1000);
})
);
});
*/
export class PromiseQueue {
private readonly concurrency: number
private _current: number = 0
private _list: (() => Promise<any>)[] = []
constructor({ concurrency = 2 }: { concurrency: number }) {
this.concurrency = concurrency
}
add(promiseFn: () => Promise<any>) {
this._list.push(promiseFn)
this.loadNext()
}
loadNext() {
if (this._list.length === 0 || this.concurrency === this._current) return
this._current++
const fn = this._list.shift()!
const promise = fn.call(this)
promise.then(this.onLoaded.bind(this)).catch(this.onLoaded.bind(this))
}
onLoaded() {
this._current--
this.loadNext()
}
}