async - pannous/warp GitHub Wiki
The async await pattern is cancer: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
Starting an async process belongs on the caller site, note on the callee site!
Correct way of handling threads: See zig and go, hell, even Java
go figure() right
goable figure (){}; await figure() wrong
In the angle language, a concurrent call is created by the go keyword, whether in a thread or not should be of no concern when writing code! It creates a typed task object wich can be stopped etc and can be assigned to its value type through auto casting:
job=go download "https://example.com"
html=await job
(url must be a string with a scheme; a bare identifier is not a URI.)
Here await makes sure the task is started (if it was paused) and waits for its value.
(Decided 2026-10-03: ! after a function mutates in place and {…}! evaluates, so job! is not await.)
Implemented (warp, 2026-10-04): a module runs on one thread, so go f(x) runs the task to its end where it starts;
await job is then its value, and a task auto-casts to its value (job + 1). once job finishes: … (also once the download finishes: for go download(url)) runs at once, the task being done; a handler of a pause or stop
(on download.stop : …) never runs and stop job / job.stop() have nothing to stop: both are warnings (errors under
use strict).
Real threads (warp, 2026-10-04, user decision; design in notes/threads.md): go f(x) runs f on its own thread, in a
fresh instance of the program, so two tasks overlap:
f(ms) := { sleep(ms); ms }
a = go f(200)
b = go f(200)
await a + await b
A task can also stand for its value (a + b), but then the program waits there without saying so: warp warns
(a is used as its value: the program waits for the task here; write await a to say so), an error under use strict.
Values are copied, never shared: the arguments are copied into the task and the result is copied back by await (or
by using the task as its value). The task sees the program's initial variables, not later changes of the caller, and
an error inside the task is the error of await (task f: index out of range), which try catches. A task nobody
awaits still finishes before the program ends. Numbers, texts, characters, lists and functions (with what they
capture) cross; an exact number beyond the 63-bit integers (a ratio like 1.5, a big integer) is a clear error for now:
shout = s => s + "!"
apply(g, v) := g(v)
await go apply(shout, "hey")
stop job, pause job and resume job control a running task; awaiting a stopped one is the error
task f: task stopped. A task pauses where a loop starts, natively and in the browser:
spin(n) := { i = 0; while i < n { i += 1 }; i }
job = go spin(3000000)
pause job
resume job
await job
once job finishes: and on job.stop: run at the first statement that sees the event. In the browser tasks run on
Workers (the playground isolates itself for shared memory).
Shared arrays are the one thing tasks share (P44): shared hits = int[1] makes Ints every task reaches (float[n]
makes floats), go count(hits) passes the same array, and hits#1 += 1 adds atomically:
shared hits = int[1]
count(xs) := { for i in 0..1000 { xs#1 += 1 }; 0 }
a = go count(hits)
b = go count(hits)
await a
await b
hits#1
A block starts as a task too: go { … } runs while the program goes on, and the program waits for its tasks before
it ends. The block gets copies of the variables it reads, as they are when it starts:
def hi: {sleep(1000 ms); print("hi")}
go {
hi()
}
print("faster")
prints faster, then hi a second later. sleep takes a duration (sleep 1s, sleep(2 seconds)) or milliseconds.
job = go { … } names the task, jobs.add(go { … }) collects tasks, and shared n = 0 is a value every task shares
(P106), as shared xs = int[n] shares an array.
The go keyword implicitly creates signal handlers which can be listened to via once etc.
English handlers that name the callee (once the download finishes) are not yet wired without a prior go download(...) bound to a job; use once job finishes: / on job.stop: on a task value (see examples above):
# not yet: bare verb / English task name without an in-scope job (card once-english-task)
once the download finishes: print "done!"
once the download pauses: print "holup!"
on download.stop : print "cancelled"
See evaluation