@azerum/ts-csp
    Preparing search index...

    Function select

    • Like select {} statement in Go: allows to concurrently try to write into/read from multiple channels, whichever is writable/readable first

      Extended to support any promises: takes in operations (those can be of different types, e.g. Promise), waits for the first one to complete, returns its result, and cancels the remaining ones

      Operation types:

      • Promise<T> - waits for the promise to resolve/reject. Cannot be cancelled

      • (signal: AbortSignal) => Promise<T> - called "abortable function". Wait for the returned promise. Can be cancelled: when this operation looses the race, signal is aborted

      • Selectable<T> - used by ReadableChannel.raceRead, WritableChannel.raceWrite.

      • null - noop that never wins the race. Useful for conditional operations, e.g. select({ maybeOp: doOp ? op() : null })

      Read from channel a or b, whichever is readable first:

      await select({ a: a.raceRead(), b: b.raceRead() })
      

      Compare this to:

      await Promise.race([a.read(), b.read()])
      

      The former reads either from a or b, but never from both. The later reads from both, returns the value of the one that wins the race, but does not cancel the read. If a wins, a value from b is lost

      ReadableChannel.raceRead returns a Selectable which allows cancellation, unlike ReadableChannel.read, which returns a Promise and cannot be cancelled

      Similarly, writes can be raced with

      await select({ a: a.raceWrite(value1), b: b.raceWrite(value2) })
      
      await select({ 
      didRead: ch.raceRead(),
      timedOut: s => sleep(1000, undefined, s)
      })

      Note that this aborts the timer if reading wins the race. If you want to keep the timeout for multiple operations, pass Promise instead of function:

      const timeout = sleep(1000, undefined)

      while (true) {
      await select({
      didRead: ch.raceRead(),
      timedOut: timeout
      })
      }
      await select({
      didRead: ch.raceRead(),
      aborted: returnOnAbort(signal),
      })
      await select({ a: promiseA, b: promiseB })
      

      Compare with:

      await Promise.race([promiseA, promiseB])
      

      If promiseA and promiseB are both settled, Promise.race will always choose promiseA, whereas select will choose at random

      • Similar to Promise.race, resolves once any operation completes successfully, rejects one any operation fails (throws)

      • "Operation fails" means:

        • for Promise<T> - promise rejects
        • for (signal?: AbortSignal) => Promise<T> - the returned promise rejects
        • for Selectable<T> - Selectable.wait or Selectable.attempt throw
        • null operations never complete, therefore never fail
      • Exception thrown by operation is wrapped in SelectError, which has SelectError.argName to tell which operation has failed

      • If multiple operations are ready, randomly selects which one wins the race

      • "Operation is ready" means:

        • for Promise<T> - promise is settled
        • for `(signal?: AbortSignal) => Promise - the returned promise is settled
        • for Selectable<T> - promise returned by {@link Selectable.wait} is settled
        • null operations are never ready

      Type Parameters

      Parameters

      Returns Promise<SelectResult<TOps>>