Skip to content

Commit ecc296a

Browse files
committed
Add a closedValue option
1 parent d122ca4 commit ecc296a

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Add `timeout` and `timeoutValue` options to
66
`SyncMessagePort.receiveMessage()`.
77

8+
* Add a `closedValue` option to `SyncMessagePort.receiveMessage()`.
9+
810
## 1.0.0
911

1012
* Initial release

lib/index.test.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ describe('SyncMessagePort', () => {
245245
await new Promise(resolve => port2.once('close', resolve));
246246
});
247247

248-
it('receiveMessage() throws an error for a closed port', () => {
248+
it("receiveMessage() throws an error for a port that's already closed", () => {
249249
const channel = SyncMessagePort.createChannel();
250250
const port1 = new SyncMessagePort(channel.port1);
251251
const port2 = new SyncMessagePort(channel.port2);
@@ -254,6 +254,52 @@ describe('SyncMessagePort', () => {
254254
expect(port1.receiveMessage).toThrow();
255255
expect(port2.receiveMessage).toThrow();
256256
});
257+
258+
it('receiveMessage() throws an error when a port closes', () => {
259+
const channel = SyncMessagePort.createChannel();
260+
const port = new SyncMessagePort(channel.port1);
261+
262+
spawnWorker(
263+
`
264+
setTimeout(() => {
265+
port.close();
266+
}, 100);
267+
`,
268+
channel.port2,
269+
);
270+
271+
expect(port.receiveMessage).toThrow();
272+
});
273+
274+
it(
275+
"receiveMessage() returns option.closedValue for a port that's " +
276+
'already closed',
277+
() => {
278+
const channel = SyncMessagePort.createChannel();
279+
const port1 = new SyncMessagePort(channel.port1);
280+
const port2 = new SyncMessagePort(channel.port2);
281+
282+
port1.close();
283+
expect(port1.receiveMessage({closedValue: 'closed'})).toBe('closed');
284+
expect(port2.receiveMessage({closedValue: 'closed'})).toBe('closed');
285+
},
286+
);
287+
288+
it('receiveMessage() throws an error when a port closes', () => {
289+
const channel = SyncMessagePort.createChannel();
290+
const port = new SyncMessagePort(channel.port1);
291+
292+
spawnWorker(
293+
`
294+
setTimeout(() => {
295+
port.close();
296+
}, 100);
297+
`,
298+
channel.port2,
299+
);
300+
301+
expect(port.receiveMessage({closedValue: 'closed'})).toBe('closed');
302+
});
257303
});
258304
});
259305

lib/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ export interface ReceiveMessageOptions {
5252
* is returned. Ignored if {@link timeout} is not set.
5353
*/
5454
timeoutValue?: unknown;
55+
56+
/**
57+
* If the underlying channel is closed before calling {@link
58+
* SyncMessagePort.receiveMessage} or while a call is pending, return this
59+
* value.
60+
*/
61+
closedValue?: unknown;
5562
}
5663

5764
/**
@@ -159,16 +166,13 @@ export class SyncMessagePort extends EventEmitter {
159166
return receiveMessageOnPort(this.port);
160167
}
161168

162-
// TODO(nex3):
163-
// * Add an option to `receiveMessage()` to return a special value if the
164-
// channel is closed.
165-
166169
/**
167170
* Blocks and returns the next message sent by the other port.
168171
*
169172
* This may not be called while this has a listener for the `'message'` event.
170173
* Throws an error if the channel is closed, including if it closes while this
171-
* is waiting for a message.
174+
* is waiting for a message, unless {@link ReceiveMessageOptions.closedValue}
175+
* is passed.
172176
*/
173177
receiveMessage(options?: ReceiveMessageOptions): unknown {
174178
if (this.listenerCount('message')) {
@@ -190,6 +194,7 @@ export class SyncMessagePort extends EventEmitter {
190194
BufferState.AwaitingMessage,
191195
);
192196
if (previousState === BufferState.Closed) {
197+
if (options && 'closedValue' in options) return options.closedValue;
193198
throw new Error("The SyncMessagePort's channel is closed.");
194199
}
195200

@@ -217,6 +222,7 @@ export class SyncMessagePort extends EventEmitter {
217222
const oldState = Atomics.and(this.buffer, 0, BufferState.Closed);
218223
// Assert the old state was either 0b10 or 0b11.
219224
assert.equal(oldState & BufferState.Closed, BufferState.Closed);
225+
if (options && 'closedValue' in options) return options.closedValue;
220226
throw new Error("The SyncMessagePort's channel is closed.");
221227
}
222228

0 commit comments

Comments
 (0)