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(promiseFn: () => Promise, options: RetryOptions): Promise { let retries = 0 let defaultOptions = { maxRetries: 3, whitelistErrors: [], } Object.assign(defaultOptions, options) const { maxRetries, whitelistErrors } = options const retryPromise = async (): Promise => { 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 { const deferred = new Deferred(); setTimeout(() => { deferred.resolve(); }, ms); return deferred.promise; } console.log("start"); delay(1000).then(() => { console.log("after 1 second"); }); console.log("end"); */ export class Deferred { private _resolve!: (value: T | PromiseLike) => void private _reject!: (reason?: any) => void public readonly promise: Promise constructor() { this.promise = new Promise((resolve, reject) => { this._resolve = resolve this._reject = reject }) } public resolve(value: T | PromiseLike): void { this._resolve(value) } public reject(reason?: any): void { this._reject(reason) } public then( onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined, ): Promise { return this.promise.then(onfulfilled, onrejected) } public catch( onrejected?: ((reason: any) => TResult | PromiseLike) | null | undefined, ): Promise { 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)[] = [] constructor({ concurrency = 2 }: { concurrency: number }) { this.concurrency = concurrency } add(promiseFn: () => Promise) { 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() } }