@tai-kun/surrealdb
Version:
The SurrealDB SDK for JavaScript
182 lines (160 loc) • 5.42 kB
text/typescript
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#state)
*/
export type StatefulPromiseState = "pending" | "fulfilled" | "rejected";
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#constructor)
*/
export type StatefulPromiseExecutor<TValue> = (
resolve: (value: TValue | PromiseLike<TValue>) => void,
reject: (reason: unknown) => void,
) => void;
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/)
*/
export default class StatefulPromise<TValue> implements PromiseLike<TValue> {
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#resolve)
*/
static resolve<TValue = void>(value?: TValue | PromiseLike<TValue>) {
if (value && typeof value === "object" && value.constructor === this) {
return value as never;
}
return new this<TValue>(resolve => resolve(value!));
}
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#reject)
*/
static reject<TValue = never>(reason?: unknown): StatefulPromise<TValue> {
return new this<TValue>((_, reject) => reject(reason));
}
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#withresolvers)
*/
static withResolvers<TValue>(): {
promise: StatefulPromise<TValue>;
resolve: (value: TValue | PromiseLike<TValue>) => void;
reject: (reason: unknown) => void;
} {
let resolve: (value: TValue | PromiseLike<TValue>) => void;
let reject: (reason: unknown) => void;
const promise = new this<TValue>((res, rej) => {
resolve = res;
reject = rej;
});
return {
promise,
resolve: resolve!,
reject: reject!,
};
}
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#try)
*/
static try<TValue, TArgs extends readonly unknown[]>(
func: (...args: TArgs) => TValue | PromiseLike<TValue>,
...args: TArgs
): StatefulPromise<TValue> {
return new this<TValue>((resolve, reject) => {
try {
resolve(func(...args));
} catch (e) {
reject(e);
}
});
}
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#allrejected)
*/
static allRejected(promises: Iterable<unknown>): StatefulPromise<unknown[]>;
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#allrejected)
*/
static allRejected<TItem>(
promises: Iterable<TItem>,
extract: (item: TItem) => unknown,
): StatefulPromise<unknown[]>;
static allRejected(
promises: Iterable<unknown>,
extract: (item: unknown) => unknown = x => x,
): StatefulPromise<unknown[]> {
return new this<unknown[]>(resolve => {
const items = Array.from(promises);
if (items.length <= 0) {
return resolve([]);
}
const errors: unknown[] = [];
let remaining = items.length;
for (let i = 0, len = items.length; i < len; i++) {
tick(extract(items[i]));
}
function tick(item?: unknown): void {
if (item instanceof StatefulPromise) {
item.then(tick, e => errors.push(e) && tick());
} else if (--remaining <= 0) {
resolve(errors);
}
}
});
}
protected _value: any;
protected _state: StatefulPromiseState = "pending";
protected _promise: Promise<void> | null;
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#constructor)
*/
constructor(executor: StatefulPromiseExecutor<TValue>) {
this._promise = new Promise<TValue>(executor).then(
value => {
this._value = value;
this._state = "fulfilled";
},
reason => {
this._value = reason;
this._state = "rejected";
},
);
}
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#state)
*/
get state(): StatefulPromiseState {
return this._state;
}
/**
* [API Reference](https://tai-kun.github.io/surrealdb.js/v2/api/utils/stateful-promise/#then)
*/
then<TFulfilledValue = TValue, TRejectedValue = never>(
onFulfilled?:
| ((value: TValue) => TFulfilledValue | PromiseLike<TFulfilledValue>)
| undefined
| null,
onRejected?:
| ((reason: unknown) => TRejectedValue | PromiseLike<TRejectedValue>)
| undefined
| null,
): StatefulPromise<TFulfilledValue | TRejectedValue> {
const This = this.constructor as typeof StatefulPromise<
TFulfilledValue | TRejectedValue
>;
return new This((resolve, reject) => {
(this._promise || Promise.resolve())
.then(() => {
// 不要になった Promise オブジェクトが GC によってメモリから開放されることを促すために、
// `this._promise` を `null` に設定します。
this._promise = null;
onFulfilled ||= x => x as any;
onRejected ||= x => {
throw x;
};
return this._state === "fulfilled"
? onFulfilled(this._value)
: onRejected(this._value);
})
.then(resolve, reject);
});
}
get [Symbol.toStringTag](): string {
return "StatefulPromise";
}
}