Tries to perform the operation. Returns either successful result with a value or a failed result
Note: callers must not mutate the returned value. The allows implementors to cache values
Waits until operation can be performed. Should resolve with value value.
If signal is aborted, should throw error, preferably AbortedError
for consistent style with other selectables
Operation that can be used with select. Implemented by ReadableChannel.raceRead and WritableChannel.raceWrite
Those operations cannot be modeled as a simple
Promiseas we have to separate "wait until operation can be performed" from "perform the operation", so select can choose which operation is performed. See Motivation section for detailsHas two stages:
Wait until the operation can be performed using Selectable.wait. E.g. for read from a channel, wait until the channel is readable
Attempt to perform the operation using Selectable.attempt. This may fail due to races, when somebody else performs the operation between the wait and the attempt
If Selectable.attempt fails, select re-runs Selectable.wait and tries again
Motivation
Once
Promiseresolves, there is not easy way to cancel it. For example, once ReadableChannel.read resolves, there is no way to put the value back into the channel, into the exact same place in the bufferWhen we want to read from one of the two channels, whichever is first, we can't use
select({ a: a.read(), b: b.read() })for this reason: if botha.read()andb.read()resolve at the same time, select has no way to cancel one of themNote that passing
AbortSignalinto ReadableChannel.read helps to cancel the read, but does not help here: if botha.read()andb.read()resolve at the same time, before signal is aborted, we have the same problemInstead, we separate "wait until operation can be done" and "perform operation". Then select can wait for all reads, select one of them, and perform only one. That's what
select({ a: a.raceRead(), b: raceRead() })doesSelectable is an interface for such a two-step operation
Note that due to the separation, race conditions are possible when between the "wait" and "perform" somebody else does the operation. E.g. channel read is implemented as
Race is possible:
channelis emptychannel.waitUntilReadable().then(() => channel.tryRead())channel. It is now readablewaitUntilReadable()resolves. It schedules the callback into the microtask queuechannel.read(), stealing the written valuetryRead(), but that returnsundefinedaschannelis now empty