Skip to content

feat(syncpromise): trim down SyncPromise #4319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions packages/utils/src/promisebuffer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
import { SentryError } from './error';
import { SyncPromise } from './syncpromise';

function allPromises<U = unknown>(collection: Array<U | PromiseLike<U>>): PromiseLike<U[]> {
return new SyncPromise<U[]>((resolve, reject) => {
if (collection.length === 0) {
resolve(null);
return;
}

let counter = collection.length;
collection.forEach(item => {
void SyncPromise.resolve(item)
.then(() => {
// eslint-disable-next-line no-plusplus
if (--counter === 0) {
resolve(null);
}
})
.then(null, reject);
});
});
}

/** A simple queue that holds promises. */
export class PromiseBuffer<T> {
/** Internal set of queued Promises */
Expand Down Expand Up @@ -85,14 +106,10 @@ export class PromiseBuffer<T> {
}, timeout);

// if all promises resolve in time, cancel the timer and resolve to `true`
void SyncPromise.all(this._buffer)
.then(() => {
clearTimeout(capturedSetTimeout);
resolve(true);
})
.then(null, () => {
resolve(true);
});
void allPromises(this._buffer).then(() => {
clearTimeout(capturedSetTimeout);
resolve(true);
});
});
}
}
116 changes: 27 additions & 89 deletions packages/utils/src/syncpromise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { isThenable } from './is';
/** SyncPromise internal states */
const enum States {
/** Pending */
PENDING = 'PENDING',
PENDING = 0,
/** Resolved / OK */
RESOLVED = 'RESOLVED',
RESOLVED = 1,
/** Rejected / Error */
REJECTED = 'REJECTED',
REJECTED = 2,
}

/**
Expand All @@ -20,11 +20,7 @@ const enum States {
*/
class SyncPromise<T> implements PromiseLike<T> {
private _state: States = States.PENDING;
private _handlers: Array<{
done: boolean;
onfulfilled?: ((value: T) => T | PromiseLike<T>) | null;
onrejected?: ((reason: any) => any) | null;
}> = [];
private _handlers: Array<[boolean, (value: T) => void, (reason: any) => any]> = [];
private _value: any;

public constructor(
Expand All @@ -51,75 +47,40 @@ class SyncPromise<T> implements PromiseLike<T> {
});
}

/** JSDoc */
public static all<U = any>(collection: Array<U | PromiseLike<U>>): PromiseLike<U[]> {
return new SyncPromise<U[]>((resolve, reject) => {
if (!Array.isArray(collection)) {
reject(new TypeError(`Promise.all requires an array as input.`));
return;
}

if (collection.length === 0) {
resolve([]);
return;
}

let counter = collection.length;
const resolvedCollection: U[] = [];

collection.forEach((item, index) => {
void SyncPromise.resolve(item)
.then(value => {
resolvedCollection[index] = value;
counter -= 1;

if (counter !== 0) {
return;
}
resolve(resolvedCollection);
})
.then(null, reject);
});
});
}

/** JSDoc */
public then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
): PromiseLike<TResult1 | TResult2> {
return new SyncPromise((resolve, reject) => {
this._attachHandler({
done: false,
onfulfilled: result => {
this._handlers.push([
false,
result => {
if (!onfulfilled) {
// TODO: ¯\_(ツ)_/¯
// TODO: FIXME
resolve(result as any);
return;
}
try {
resolve(onfulfilled(result));
return;
} catch (e) {
reject(e);
return;
} else {
try {
resolve(onfulfilled(result));
} catch (e) {
reject(e);
}
}
},
onrejected: reason => {
reason => {
if (!onrejected) {
reject(reason);
return;
}
try {
resolve(onrejected(reason));
return;
} catch (e) {
reject(e);
return;
} else {
try {
resolve(onrejected(reason));
} catch (e) {
reject(e);
}
}
},
});
]);
this._executeHandlers();
});
}

Expand Down Expand Up @@ -162,11 +123,6 @@ class SyncPromise<T> implements PromiseLike<T> {
});
}

/** JSDoc */
public toString(): string {
return '[object SyncPromise]';
}

/** JSDoc */
private readonly _resolve = (value?: T | PromiseLike<T> | null) => {
this._setResult(States.RESOLVED, value);
Expand Down Expand Up @@ -194,20 +150,6 @@ class SyncPromise<T> implements PromiseLike<T> {
this._executeHandlers();
};

// TODO: FIXME
/** JSDoc */
private readonly _attachHandler = (handler: {
/** JSDoc */
done: boolean;
/** JSDoc */
onfulfilled?(value: T): any;
/** JSDoc */
onrejected?(reason: any): any;
}) => {
this._handlers = this._handlers.concat(handler);
this._executeHandlers();
};

/** JSDoc */
private readonly _executeHandlers = () => {
if (this._state === States.PENDING) {
Expand All @@ -218,24 +160,20 @@ class SyncPromise<T> implements PromiseLike<T> {
this._handlers = [];

cachedHandlers.forEach(handler => {
if (handler.done) {
if (handler[0]) {
return;
}

if (this._state === States.RESOLVED) {
if (handler.onfulfilled) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handler.onfulfilled((this._value as unknown) as any);
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handler[1]((this._value as unknown) as any);
}

if (this._state === States.REJECTED) {
if (handler.onrejected) {
handler.onrejected(this._value);
}
handler[2](this._value);
}

handler.done = true;
handler[0] = true;
});
};
}
Expand Down