Skip to content

Commit 36e5361

Browse files
committed
fix: substantially clarify the awaitWithTimeout interface
1 parent 791a7f0 commit 36e5361

File tree

3 files changed

+70
-43
lines changed

3 files changed

+70
-43
lines changed

src/subscriber.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -991,14 +991,18 @@ export class Subscriber extends EventEmitter {
991991
this._inventory.on('empty', r);
992992
});
993993

994-
try {
995-
await awaitWithTimeout(emptyPromise, waitTimeout);
996-
} catch (e) {
994+
const resultCompletion = await awaitWithTimeout(
995+
emptyPromise,
996+
waitTimeout,
997+
);
998+
if (resultCompletion.exception || resultCompletion.timedOut) {
997999
// Don't try to deal with errors at this point, just warn-log.
998-
const err = e as [unknown, boolean];
999-
if (err[1] === false) {
1000+
if (resultCompletion.timedOut === false) {
10001001
// This wasn't a timeout.
1001-
logs.debug.warn('Error during Subscriber.close(): %j', err[0]);
1002+
logs.debug.warn(
1003+
'Error during Subscriber.close(): %j',
1004+
resultCompletion.exception,
1005+
);
10021006
}
10031007
}
10041008
}
@@ -1019,14 +1023,15 @@ export class Subscriber extends EventEmitter {
10191023

10201024
// Wait for user callbacks to complete.
10211025
const flushCompleted = this._waitForFlush();
1022-
try {
1023-
await awaitWithTimeout(flushCompleted, timeout);
1024-
} catch (e) {
1026+
const flushResult = await awaitWithTimeout(flushCompleted, timeout);
1027+
if (flushResult.exception || flushResult.timedOut) {
10251028
// Don't try to deal with errors at this point, just warn-log.
1026-
const err = e as [unknown, boolean];
1027-
if (err[1] === false) {
1029+
if (flushResult.timedOut === false) {
10281030
// This wasn't a timeout.
1029-
logs.debug.warn('Error during Subscriber.close(): %j', err[0]);
1031+
logs.debug.warn(
1032+
'Error during Subscriber.close(): %j',
1033+
flushResult.exception,
1034+
);
10301035
}
10311036
}
10321037

src/util.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,29 +88,48 @@ export function addToBucket<T, U>(map: Map<T, U[]>, bucket: T, item: U) {
8888
const timeoutToken: unique symbol = Symbol('pubsub promise timeout');
8989

9090
/**
91-
* Awaits on Promise with a timeout. Resolves on success, rejects on timeout.
91+
* Return value from `awaitWithTimeout`. There are several variations on what
92+
* might happen here, so this bundles it all up into a "report".
93+
*/
94+
export interface TimeoutReturn<T> {
95+
returnedValue?: T;
96+
exception?: Error;
97+
timedOut: boolean;
98+
}
99+
100+
/**
101+
* Awaits on Promise with a timeout. Resolves on the passed promise resolving or
102+
* rejecting, or on timeout.
92103
*
93104
* @param promise An existing Promise returning type T.
94105
* @param timeout A timeout value as a Duration; if the timeout is exceeded while
95-
* waiting for the promise, the Promise this function returns will reject.
96-
* @returns In any case, a tuple with the first item being the T value or an
97-
* error value, and the second item being true if the timeout was exceeded.
106+
* waiting for the promise, the Promise this function returns will resolve, but
107+
* with `timedOut` set.
108+
* @returns A TimeoutReturn with the returned value, if applicable, an exception if
109+
* the promise rejected, or the timedOut set to true if it timed out.
98110
*/
99111
export async function awaitWithTimeout<T>(
100112
promise: Promise<T>,
101113
timeout: Duration,
102-
): Promise<[T, boolean]> {
114+
): Promise<TimeoutReturn<T>> {
103115
let timeoutId: NodeJS.Timeout | undefined;
104116
const timeoutPromise = new Promise<T>((_, rej) => {
105117
timeoutId = setTimeout(() => rej(timeoutToken), timeout.milliseconds);
106118
});
107119
try {
108120
const value = await Promise.race([timeoutPromise, promise]);
109121
clearTimeout(timeoutId);
110-
return [value, false];
122+
return {
123+
returnedValue: value,
124+
timedOut: false,
125+
};
111126
} catch (e) {
112-
// The timeout passed.
127+
const err: Error | symbol = e as unknown as Error | symbol;
128+
// The timeout passed or the promise rejected.
113129
clearTimeout(timeoutId);
114-
throw [e, e === timeoutToken];
130+
return {
131+
exception: (err !== timeoutToken ? err : undefined) as Error | undefined,
132+
timedOut: err === timeoutToken,
133+
};
115134
}
116135
}

test/util.ts

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,15 @@ describe('utils', () => {
8383
Duration.from({seconds: 1}),
8484
);
8585
fakeTimers.tick(500);
86-
try {
87-
const result = await awaitPromise;
88-
assert.deepStrictEqual(result, [testString, false]);
89-
} catch (e) {
90-
assert.strictEqual(e, null, 'timeout was triggered, improperly');
91-
}
86+
87+
const result = await awaitPromise;
88+
assert.strictEqual(result.returnedValue, testString);
89+
assert.strictEqual(result.exception, undefined);
90+
assert.strictEqual(
91+
result.timedOut,
92+
false,
93+
'timeout was triggered, improperly',
94+
);
9295
});
9396

9497
it('handles non-timeout errors properly', async () => {
@@ -104,12 +107,14 @@ describe('utils', () => {
104107
Duration.from({seconds: 1}),
105108
);
106109
fakeTimers.tick(500);
107-
try {
108-
const result = await awaitPromise;
109-
assert.strictEqual(result, null, 'non-error was triggered, improperly');
110-
} catch (e) {
111-
assert.deepStrictEqual(e, [testString, false]);
112-
}
110+
111+
const result = await awaitPromise;
112+
assert.strictEqual(
113+
result.exception,
114+
testString,
115+
'non-error was triggered, improperly',
116+
);
117+
assert.strictEqual(result.timedOut, false);
113118
});
114119

115120
it('handles timeout properly', async () => {
@@ -125,17 +130,15 @@ describe('utils', () => {
125130
Duration.from({seconds: 1}),
126131
);
127132
fakeTimers.tick(1500);
128-
try {
129-
const result = await awaitPromise;
130-
assert.strictEqual(
131-
result,
132-
null,
133-
'timeout was not triggered, improperly',
134-
);
135-
} catch (e) {
136-
const err: unknown[] = e as unknown[];
137-
assert.strictEqual(err[1], true);
138-
}
133+
134+
const result = await awaitPromise;
135+
assert.strictEqual(
136+
result.timedOut,
137+
true,
138+
'timeout was not triggered, improperly',
139+
);
140+
141+
assert.strictEqual(result.timedOut, true);
139142
});
140143
});
141144
});

0 commit comments

Comments
 (0)