Concurrent Jobs

A batch of jobs that each spend most of their time waiting, run a few at a time instead of one after another. The tests check how long the batch took, down to the second, without waiting at all.

Start first, await later

run is an @async function, so calling it starts the job and gives back a Promise<String> straight away. runAll starts one lane per slot with (0..<lanes) ||> lane. That's an array of promises, all of them already running, and Promise.all waits for the lot.

Inside a lane, await run(job) waits for each job before starting the next, so a lane never has more than one job going. Each lane runs in its own task, and every lane is paused at an await most of the time, so they take turns on the one thread without getting in each other's way.

Order comes back by index

Lanes finish in whatever order their jobs allow, so each lane keeps the index of every job it ran. flatten joins the lanes' lists, sortedBy puts them back in the jobs' order, and the last map drops the index. The lambdas take their tuple apart in the parameter, λ((i, _)) i.

Time without waiting

Promise.sleep goes by &clock. When no task is due, the event loop sleeps the clock until the next one is. The tests inject a FixedClock, whose sleep moves the time forward at once. The first test can then check that four jobs taking 7 seconds in all finish in exactly 4 seconds of clock time on two lanes, and it runs instantly.

The second test gives up on a five-minute job after ten seconds. timeout gives a Promise<TimeoutError ! String>, and the match checks which it got. The slow job itself isn't cancelled. It keeps its timer, and the test ends without waiting for it.

Concepts shown

  • @async functions, including one defined inside another, and await.
  • Starting several promises before awaiting any, and Promise.all.
  • timeout and matching on TimeoutError.
  • @async @main and @test functions, with FixedClock controlling time.