mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-04 10:19:17 +02:00
refactor(struct): 10% performance improvement
This commit is contained in:
parent
fa8d251bf2
commit
ff708def46
9 changed files with 176 additions and 234 deletions
|
@ -54,6 +54,7 @@ const state = makeAutoObservable({
|
||||||
logcat: undefined as Logcat | undefined,
|
logcat: undefined as Logcat | undefined,
|
||||||
running: false,
|
running: false,
|
||||||
buffer: [] as LogRow[],
|
buffer: [] as LogRow[],
|
||||||
|
flushRequested: false,
|
||||||
list: [] as LogRow[],
|
list: [] as LogRow[],
|
||||||
count: 0,
|
count: 0,
|
||||||
stream: undefined as ReadableStream<LogMessage> | undefined,
|
stream: undefined as ReadableStream<LogMessage> | undefined,
|
||||||
|
@ -73,21 +74,20 @@ const state = makeAutoObservable({
|
||||||
new WritableStream({
|
new WritableStream({
|
||||||
write: (chunk) => {
|
write: (chunk) => {
|
||||||
this.buffer.push(chunk);
|
this.buffer.push(chunk);
|
||||||
|
if (!this.flushRequested) {
|
||||||
|
this.flushRequested = true;
|
||||||
|
requestAnimationFrame(this.flush);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{ signal: this.stopSignal.signal }
|
{ signal: this.stopSignal.signal }
|
||||||
)
|
)
|
||||||
.catch(() => { });
|
.catch(() => { });
|
||||||
this.flush();
|
|
||||||
},
|
},
|
||||||
flush() {
|
flush() {
|
||||||
if (this.buffer.length) {
|
|
||||||
this.list.push(...this.buffer);
|
this.list.push(...this.buffer);
|
||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
}
|
this.flushRequested = false;
|
||||||
if (this.running) {
|
|
||||||
requestAnimationFrame(this.flush);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
stop() {
|
stop() {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
|
|
|
@ -34,7 +34,8 @@ export const AdbSyncLstatResponse =
|
||||||
get permission() { return this.mode & 0b00001111_11111111; },
|
get permission() { return this.mode & 0b00001111_11111111; },
|
||||||
})
|
})
|
||||||
.postDeserialize((object) => {
|
.postDeserialize((object) => {
|
||||||
if (object.mode === 0 &&
|
if (
|
||||||
|
object.mode === 0 &&
|
||||||
object.size === 0 &&
|
object.size === 0 &&
|
||||||
object.mtime === 0
|
object.mtime === 0
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -26,38 +26,25 @@ export class BufferedStream {
|
||||||
this.reader = stream.getReader();
|
this.reader = stream.getReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async readSource() {
|
||||||
*
|
const { done, value } = await this.reader.read();
|
||||||
* @param length
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public async read(length: number): Promise<Uint8Array> {
|
|
||||||
let result: Uint8Array;
|
|
||||||
let index: number;
|
|
||||||
|
|
||||||
if (this.buffered) {
|
|
||||||
let array = this.buffered;
|
|
||||||
const offset = this.bufferedOffset;
|
|
||||||
if (this.bufferedLength > length) {
|
|
||||||
// PERF: `subarray` is slow
|
|
||||||
// don't use it until absolutely necessary
|
|
||||||
this.bufferedOffset += length;
|
|
||||||
this.bufferedLength -= length;
|
|
||||||
return array.subarray(offset, offset + length);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.buffered = undefined;
|
|
||||||
array = array.subarray(offset);
|
|
||||||
result = new Uint8Array(length);
|
|
||||||
result.set(array);
|
|
||||||
index = this.bufferedLength;
|
|
||||||
length -= this.bufferedLength;
|
|
||||||
} else {
|
|
||||||
const { done, value: array } = await this.reader.read();
|
|
||||||
if (done) {
|
if (done) {
|
||||||
throw new BufferedStreamEndedError();
|
throw new BufferedStreamEndedError();
|
||||||
}
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async readAsync(length: number, initial?: Uint8Array) {
|
||||||
|
let result: Uint8Array;
|
||||||
|
let index: number;
|
||||||
|
|
||||||
|
if (initial) {
|
||||||
|
result = new Uint8Array(length);
|
||||||
|
result.set(initial);
|
||||||
|
index = initial.byteLength;
|
||||||
|
length -= initial.byteLength;
|
||||||
|
} else {
|
||||||
|
const array = await this.readSource();
|
||||||
if (array.byteLength === length) {
|
if (array.byteLength === length) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
@ -76,11 +63,7 @@ export class BufferedStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
const { done, value: array } = await this.reader.read();
|
const array = await this.readSource();
|
||||||
if (done) {
|
|
||||||
throw new BufferedStreamEndedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array.byteLength === length) {
|
if (array.byteLength === length) {
|
||||||
result.set(array, index);
|
result.set(array, index);
|
||||||
return result;
|
return result;
|
||||||
|
@ -102,6 +85,31 @@ export class BufferedStream {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public read(length: number): Uint8Array | Promise<Uint8Array> {
|
||||||
|
// PERF: Add a synchronous path for reading from internal buffer
|
||||||
|
if (this.buffered) {
|
||||||
|
const array = this.buffered;
|
||||||
|
const offset = this.bufferedOffset;
|
||||||
|
if (this.bufferedLength > length) {
|
||||||
|
// PERF: `subarray` is slow
|
||||||
|
// don't use it until absolutely necessary
|
||||||
|
this.bufferedOffset += length;
|
||||||
|
this.bufferedLength -= length;
|
||||||
|
return array.subarray(offset, offset + length);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buffered = undefined;
|
||||||
|
return this.readAsync(length, array.subarray(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.readAsync(length);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a readable stream with unconsumed data (if any) and
|
* Return a readable stream with unconsumed data (if any) and
|
||||||
* all data from the wrapped stream.
|
* all data from the wrapped stream.
|
||||||
|
|
|
@ -645,13 +645,11 @@ struct.field(
|
||||||
|
|
||||||
### Relationship between types
|
### Relationship between types
|
||||||
|
|
||||||
A `Struct` is a map between keys and `StructFieldDefinition`s.
|
* `StructFieldValue`: Contains value of a field, with optional metadata and accessor methods.
|
||||||
|
* `StructFieldDefinition`: Definition of a field, can deserialize `StructFieldValue`s from a stream or create them from exist values.
|
||||||
A `StructValue` is a map between keys and `StructFieldValue`s.
|
* `StructValue`: A map between field names and `StructFieldValue`s.
|
||||||
|
* `Struct`: Definiton of a struct, a map between field names and `StructFieldDefintion`s. May contain extra metadata.
|
||||||
A `Struct` can create (deserialize) multiple `StructValue`s with same field definitions.
|
* Result of `Struct#deserialize()`: A map between field names and results of `StructFieldValue#get()`.
|
||||||
|
|
||||||
Each time a `Struct` deserialize, each `StructFieldDefinition` in it creates exactly one `StructFieldValue` to be put into the `StructValue`.
|
|
||||||
|
|
||||||
### `StructFieldDefinition`
|
### `StructFieldDefinition`
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,11 @@ export abstract class StructFieldValue<
|
||||||
/** Gets the associated `Struct` instance */
|
/** Gets the associated `Struct` instance */
|
||||||
public readonly struct: StructValue;
|
public readonly struct: StructValue;
|
||||||
|
|
||||||
|
public get hasCustomAccessors(): boolean {
|
||||||
|
return this.get !== StructFieldValue.prototype.get ||
|
||||||
|
this.set !== StructFieldValue.prototype.set;
|
||||||
|
}
|
||||||
|
|
||||||
protected value: TDefinition['TValue'];
|
protected value: TDefinition['TValue'];
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { ValueOrPromise } from "../utils.js";
|
||||||
|
|
||||||
export interface StructDeserializeStream {
|
export interface StructDeserializeStream {
|
||||||
/**
|
/**
|
||||||
* Read data from the underlying data source.
|
* Read data from the underlying data source.
|
||||||
|
@ -15,5 +17,5 @@ export interface StructAsyncDeserializeStream {
|
||||||
* The stream must return exactly `length` bytes or data. If that's not possible
|
* The stream must return exactly `length` bytes or data. If that's not possible
|
||||||
* (due to end of file or other error condition), it must throw an error.
|
* (due to end of file or other error condition), it must throw an error.
|
||||||
*/
|
*/
|
||||||
read(length: number): Promise<Uint8Array>;
|
read(length: number): ValueOrPromise<Uint8Array>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { StructFieldValue } from "./field-value.js";
|
import type { StructFieldValue } from "./field-value.js";
|
||||||
|
|
||||||
export const STRUCT_VALUE_SYMBOL = Symbol("struct-value");
|
export const STRUCT_VALUE_SYMBOL = Symbol("struct-value");
|
||||||
|
|
||||||
|
@ -11,9 +11,15 @@ export class StructValue {
|
||||||
/**
|
/**
|
||||||
* Gets the result struct value object
|
* Gets the result struct value object
|
||||||
*/
|
*/
|
||||||
public readonly value: Record<PropertyKey, unknown> = {};
|
public readonly value: Record<PropertyKey, unknown>;
|
||||||
|
|
||||||
public constructor() {
|
public constructor(prototype: any) {
|
||||||
|
// PERF: `Object.create(extra)` is 50% faster
|
||||||
|
// than `Object.defineProperties(this.value, extra)`
|
||||||
|
this.value = Object.create(prototype);
|
||||||
|
|
||||||
|
// PERF: `Object.defineProperty` is slow
|
||||||
|
// but we need it to be non-enumerable
|
||||||
Object.defineProperty(
|
Object.defineProperty(
|
||||||
this.value,
|
this.value,
|
||||||
STRUCT_VALUE_SYMBOL,
|
STRUCT_VALUE_SYMBOL,
|
||||||
|
@ -31,8 +37,8 @@ export class StructValue {
|
||||||
this.fieldValues[name] = fieldValue;
|
this.fieldValues[name] = fieldValue;
|
||||||
|
|
||||||
// PERF: `Object.defineProperty` is slow
|
// PERF: `Object.defineProperty` is slow
|
||||||
if (fieldValue.get !== StructFieldValue.prototype.get ||
|
// use normal property when possible
|
||||||
fieldValue.set !== StructFieldValue.prototype.set) {
|
if (fieldValue.hasCustomAccessors) {
|
||||||
Object.defineProperty(this.value, name, {
|
Object.defineProperty(this.value, name, {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
|
|
@ -490,7 +490,10 @@ export class Struct<
|
||||||
Overwrite<TExtra, T>,
|
Overwrite<TExtra, T>,
|
||||||
TPostDeserialized
|
TPostDeserialized
|
||||||
> {
|
> {
|
||||||
Object.assign(this._extra, Object.getOwnPropertyDescriptors(value));
|
Object.defineProperties(
|
||||||
|
this._extra,
|
||||||
|
Object.getOwnPropertyDescriptors(value)
|
||||||
|
);
|
||||||
return this as any;
|
return this as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,14 +543,12 @@ export class Struct<
|
||||||
public deserialize(
|
public deserialize(
|
||||||
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
stream: StructDeserializeStream | StructAsyncDeserializeStream,
|
||||||
): ValueOrPromise<StructDeserializedResult<TFields, TExtra, TPostDeserialized>> {
|
): ValueOrPromise<StructDeserializedResult<TFields, TExtra, TPostDeserialized>> {
|
||||||
const structValue = new StructValue();
|
const structValue = new StructValue(this._extra);
|
||||||
Object.defineProperties(structValue.value, this._extra);
|
|
||||||
|
let promise = SyncPromise.resolve();
|
||||||
|
|
||||||
return SyncPromise
|
|
||||||
.try(() => {
|
|
||||||
let result = SyncPromise.resolve();
|
|
||||||
for (const [name, definition] of this._fields) {
|
for (const [name, definition] of this._fields) {
|
||||||
result = result
|
promise = promise
|
||||||
.then(() =>
|
.then(() =>
|
||||||
definition.deserialize(this.options, stream as any, structValue)
|
definition.deserialize(this.options, stream as any, structValue)
|
||||||
)
|
)
|
||||||
|
@ -555,8 +556,8 @@ export class Struct<
|
||||||
structValue.set(name, fieldValue);
|
structValue.set(name, fieldValue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
})
|
return promise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const object = structValue.value;
|
const object = structValue.value;
|
||||||
|
|
||||||
|
@ -588,7 +589,7 @@ export class Struct<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
structValue = new StructValue();
|
structValue = new StructValue({});
|
||||||
for (const [name, definition] of this._fields) {
|
for (const [name, definition] of this._fields) {
|
||||||
const fieldValue = definition.create(
|
const fieldValue = definition.create(
|
||||||
this.options,
|
this.options,
|
||||||
|
|
|
@ -1,185 +1,106 @@
|
||||||
enum State {
|
export interface SyncPromise<T> {
|
||||||
Pending,
|
then<TResult1 = T, TResult2 = never>(
|
||||||
Fulfilled,
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
|
||||||
Rejected,
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
|
||||||
|
): SyncPromise<TResult1 | TResult2>;
|
||||||
|
|
||||||
|
valueOrPromise(): T | PromiseLike<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fulfilledThen<T, TResult1 = T>(
|
interface SyncPromiseStatic {
|
||||||
this: SyncPromise<T>,
|
reject<T = never>(reason?: any): SyncPromise<T>;
|
||||||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
||||||
): SyncPromise<TResult1> {
|
resolve(): SyncPromise<void>;
|
||||||
if (onfulfilled) {
|
resolve<T>(value: T | PromiseLike<T>): SyncPromise<T>;
|
||||||
return SyncPromise.try(() => onfulfilled(this.result as T));
|
|
||||||
}
|
try<T>(executor: () => T | PromiseLike<T>): SyncPromise<T>;
|
||||||
return this as unknown as SyncPromise<TResult1>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function rejectedThen<T, TResult1 = T, TResult2 = never>(
|
export const SyncPromise: SyncPromiseStatic = {
|
||||||
this: SyncPromise<T>,
|
reject<T = never>(reason?: any): SyncPromise<T> {
|
||||||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
return new RejectedSyncPromise(reason);
|
||||||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
|
},
|
||||||
): SyncPromise<TResult2> {
|
resolve<T>(value?: T | PromiseLike<T>): SyncPromise<T> {
|
||||||
if (onrejected) {
|
if (
|
||||||
return SyncPromise.try(() => onrejected(this.result));
|
typeof value === 'object' &&
|
||||||
|
value !== null &&
|
||||||
|
typeof (value as PromiseLike<T>).then === 'function'
|
||||||
|
) {
|
||||||
|
return new PendingSyncPromise(value as PromiseLike<T>);
|
||||||
|
} else {
|
||||||
|
return new ResolvedSyncPromise(value as T);
|
||||||
}
|
}
|
||||||
return this as unknown as SyncPromise<TResult2>;
|
},
|
||||||
}
|
try<T>(executor: () => T | PromiseLike<T>): SyncPromise<T> {
|
||||||
|
|
||||||
function fulfilledValue<T>(
|
|
||||||
this: SyncPromise<T>,
|
|
||||||
) {
|
|
||||||
return this.result as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rejectedValue<T>(
|
|
||||||
this: SyncPromise<T>,
|
|
||||||
): never {
|
|
||||||
throw this.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const INTERNAL_CREATED = Symbol('internal-created') as any;
|
|
||||||
|
|
||||||
export class SyncPromise<T> implements PromiseLike<T> {
|
|
||||||
public static reject<T = never>(reason?: any): SyncPromise<T> {
|
|
||||||
const promise = new SyncPromise<T>(INTERNAL_CREATED);
|
|
||||||
promise.handleReject(reason);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static resolve(): SyncPromise<void>;
|
|
||||||
public static resolve<T>(value: T | PromiseLike<T>): SyncPromise<T>;
|
|
||||||
public static resolve<T>(value?: T | PromiseLike<T>): SyncPromise<T> {
|
|
||||||
if (value instanceof SyncPromise) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = new SyncPromise<T>(INTERNAL_CREATED);
|
|
||||||
promise.handleResolve(value!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static try<T>(executor: () => T | PromiseLike<T>): SyncPromise<T> {
|
|
||||||
try {
|
try {
|
||||||
return SyncPromise.resolve(executor());
|
return SyncPromise.resolve(executor());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return SyncPromise.reject(e);
|
return SyncPromise.reject(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public state: State = State.Pending;
|
class PendingSyncPromise<T> implements SyncPromise<T> {
|
||||||
public result: unknown;
|
private promise: PromiseLike<T>;
|
||||||
public promise: PromiseLike<T> | undefined;
|
|
||||||
|
|
||||||
public constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
|
public constructor(promise: PromiseLike<T>) {
|
||||||
if (executor === INTERNAL_CREATED) {
|
this.promise = promise;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let promiseResolve: (value: T | PromiseLike<T>) => void;
|
|
||||||
let promiseReject: (reason?: any) => void;
|
|
||||||
|
|
||||||
let settled = false;
|
|
||||||
let sync = true;
|
|
||||||
|
|
||||||
const handleReject = (reason?: any) => {
|
|
||||||
if (settled) { return; }
|
|
||||||
settled = true;
|
|
||||||
|
|
||||||
if (!sync) {
|
|
||||||
promiseReject(reason);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleReject(reason);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
executor(
|
|
||||||
(value: T | PromiseLike<T>) => {
|
|
||||||
if (settled) { return; }
|
|
||||||
settled = true;
|
|
||||||
|
|
||||||
if (!sync) {
|
|
||||||
promiseResolve(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleResolve(value);
|
|
||||||
},
|
|
||||||
handleReject
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
handleReject(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state === State.Pending && !this.promise) {
|
|
||||||
this.promise = new Promise<T>((resolve, reject) => {
|
|
||||||
promiseResolve = resolve;
|
|
||||||
promiseReject = reject;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sync = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleResolveValue(value: T) {
|
|
||||||
this.state = State.Fulfilled;
|
|
||||||
this.result = value;
|
|
||||||
this.then = fulfilledThen;
|
|
||||||
this.valueOrPromise = fulfilledValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleResolve(value: T | PromiseLike<T>) {
|
|
||||||
if (typeof value === 'object' &&
|
|
||||||
value !== null &&
|
|
||||||
'then' in value &&
|
|
||||||
typeof value.then === 'function'
|
|
||||||
) {
|
|
||||||
if (value instanceof SyncPromise) {
|
|
||||||
switch (value.state) {
|
|
||||||
case State.Fulfilled:
|
|
||||||
this.handleResolveValue(value.result as T);
|
|
||||||
return;
|
|
||||||
case State.Rejected:
|
|
||||||
this.handleReject(value.result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.promise = value as PromiseLike<T>;
|
|
||||||
} else {
|
|
||||||
this.handleResolveValue(value as T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleReject(reason?: any) {
|
|
||||||
this.state = State.Rejected;
|
|
||||||
this.result = reason;
|
|
||||||
this.then = rejectedThen;
|
|
||||||
this.valueOrPromise = rejectedValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
public then<TResult1 = T, TResult2 = never>(
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
|
||||||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
|
||||||
): SyncPromise<TResult1 | TResult2> {
|
) {
|
||||||
// The result promise isn't really a `SyncPromise`,
|
return new PendingSyncPromise<TResult1 | TResult2>(
|
||||||
// but we attach a `valueOrPromise` to it,
|
this.promise.then(onfulfilled, onrejected)
|
||||||
// so it's compatible with `SyncPromise`.
|
);
|
||||||
// PERF: it's 230% faster than creating a real `SyncPromise` instance.
|
|
||||||
const promise =
|
|
||||||
this.promise!.then(onfulfilled, onrejected) as unknown as SyncPromise<TResult1 | TResult2>;
|
|
||||||
promise.valueOrPromise = this.valueOrPromise as unknown as () => TResult1 | TResult2 | PromiseLike<TResult1 | TResult2>;
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public catch<TResult = never>(
|
|
||||||
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined
|
|
||||||
): SyncPromise<T | TResult> {
|
|
||||||
return this.then(undefined, onrejected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public valueOrPromise(): T | PromiseLike<T> {
|
public valueOrPromise(): T | PromiseLike<T> {
|
||||||
return this;
|
return this.promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResolvedSyncPromise<T> implements SyncPromise<T> {
|
||||||
|
private value: T;
|
||||||
|
|
||||||
|
public constructor(value: T) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
|
||||||
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
|
||||||
|
) {
|
||||||
|
if (!onfulfilled) {
|
||||||
|
return this as any;
|
||||||
|
}
|
||||||
|
return SyncPromise.try(() => onfulfilled(this.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public valueOrPromise(): T | PromiseLike<T> {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RejectedSyncPromise<T> implements SyncPromise<T> {
|
||||||
|
private reason: any;
|
||||||
|
|
||||||
|
public constructor(reason: any) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
|
||||||
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
|
||||||
|
) {
|
||||||
|
if (!onrejected) {
|
||||||
|
return this as any;
|
||||||
|
}
|
||||||
|
return SyncPromise.try(() => onrejected(this.reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
public valueOrPromise(): T | PromiseLike<T> {
|
||||||
|
throw this.reason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue