Skip to content

Commit 8c56e1a

Browse files
authored
feat: add reject function to waiter (#38)
1 parent ecb40b8 commit 8c56e1a

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

src/helpers/__tests__/helpers.test.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,63 @@ describe('Helper tests', () => {
5050
expect(countListeners()).toBe(0);
5151
});
5252

53-
test('waiter is finished', async () => {
53+
test('waiter is resolved', async () => {
54+
const myWaiter = waiter();
55+
myWaiter.resolve();
56+
await myWaiter;
57+
expect(myWaiter.isFinished).toBe(true);
58+
expect(myWaiter.isRejected).toBe(false);
59+
expect(myWaiter.isResolved).toBe(true);
60+
});
61+
62+
test('waiter is resolved with value', async () => {
63+
const myWaiter = waiter<string>();
64+
const value = 'my resolve result';
65+
myWaiter.resolve(value);
66+
const result = await myWaiter;
67+
expect(result).toBe(value);
68+
expect(myWaiter.isFinished).toBe(true);
69+
expect(myWaiter.isRejected).toBe(false);
70+
expect(myWaiter.isResolved).toBe(true);
71+
});
72+
73+
test('waiter is finished (ensure finish alias works)', async () => {
5474
const myWaiter = waiter();
5575
myWaiter.finish();
5676
await myWaiter;
5777
expect(myWaiter.isFinished).toBe(true);
78+
expect(myWaiter.isRejected).toBe(false);
79+
expect(myWaiter.isResolved).toBe(true);
80+
});
81+
82+
test('waiter is rejected', async () => {
83+
const myWaiter = waiter();
84+
const error = new Error('Waiter was rejected');
85+
myWaiter.reject(error);
86+
await expect(myWaiter).rejects.toThrow(error);
87+
expect(myWaiter.isFinished).toBe(true);
88+
expect(myWaiter.isRejected).toBe(true);
89+
expect(myWaiter.isResolved).toBe(false);
90+
});
91+
92+
test('waiter is rejected with error type', async () => {
93+
class MyError extends Error {
94+
readonly name = 'MyError';
95+
}
96+
const myWaiter = waiter<void, MyError>();
97+
const error = new MyError('MyError test instance');
98+
myWaiter.reject(error);
99+
await expect(myWaiter).rejects.toThrow(error);
100+
expect(myWaiter.isFinished).toBe(true);
101+
expect(myWaiter.isRejected).toBe(true);
102+
expect(myWaiter.isResolved).toBe(false);
103+
104+
// Expect other error types to cause a typescript error
105+
class OtherError extends Error {
106+
readonly name = 'OtherError';
107+
}
108+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
109+
// @ts-expect-error
110+
myWaiter.reject(new OtherError('OtherError test instance'));
58111
});
59112
});

src/helpers/time.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,26 +105,43 @@ export function stopwatch(): Stopwatch {
105105
return result;
106106
}
107107

108-
export type Waiter<T> = Promise<T> & {
108+
export type Waiter<T = void, E = Error> = Promise<T> & {
109+
/** Alias for `resolve` */
109110
finish: (result: T) => void;
111+
resolve: (result: T) => void;
112+
reject: (error: E) => void;
113+
/** True if the promise is resolved or rejected */
110114
isFinished: boolean;
115+
/** True only if the promise is resolved */
116+
isResolved: boolean;
117+
/** True only if the promise is rejected */
118+
isRejected: boolean;
111119
};
112120

113121
/**
114-
* Creates a `Waiter` promise that can be resolved at a later time with a return value.
122+
* Creates a `Waiter` promise that can be resolved or rejected at a later time.
115123
* @returns Waiter
116124
*/
117-
export function waiter<T = void>(): Waiter<T> {
125+
export function waiter<T = void, E = Error>(): Waiter<T, E> {
118126
let resolveFn: (result: T) => void;
119-
const promise = new Promise<T>(resolve => {
127+
let rejectFn: (error: E) => void;
128+
const promise = new Promise<T>((resolve, reject) => {
120129
resolveFn = resolve;
130+
rejectFn = reject;
121131
});
122132
const completer = {
123-
finish: (result: T) => {
124-
void Object.assign(promise, { isFinished: true });
133+
finish: (result: T) => completer.resolve(result),
134+
resolve: (result: T) => {
135+
void Object.assign(promise, { isFinished: true, isResolved: true });
125136
resolveFn(result);
126137
},
138+
reject: (error: E) => {
139+
void Object.assign(promise, { isFinished: true, isRejected: true });
140+
rejectFn(error);
141+
},
127142
isFinished: false,
143+
isResolved: false,
144+
isRejected: false,
128145
};
129146
return Object.assign(promise, completer);
130147
}

0 commit comments

Comments
 (0)