Skip to content

Concurrency: do, await, sleep

Harneet provides a simple, readable concurrency model inspired by Go’s goroutines and modern async/await syntax:

  • do spawns work concurrently and returns immediately with a Task handle.
  • await(task) blocks until the Task completes and returns the function’s result.
  • sleep(ms) suspends the current task for the given milliseconds.

Under the hood, a do expression runs your call in a lightweight task and returns a Task object you can await later.


Quick start

Quick Start
package main
import fmt

function work(name string, ms int) string {
    fmt.Printf("start %s\n", name)
    sleep(ms)
    fmt.Printf("done  %s\n", name)
    return name
}

fmt.Println("\n=== Concurrency Demo: do / await / sleep ===")

var t1 = do work("A", 150)
var t2 = do work("B", 60)

var r2 = await(t2)   // B likely finishes first
var r1 = await(t1)

fmt.Printf("Results: %s %s\n", r1, r2)
fmt.Println("✅ Concurrency example complete!\n")

What happens: - do work(...) starts immediately and returns a Task handle without waiting. - await(task) waits for completion and yields the underlying return value. - sleep(ms) pauses the running task only.


API reference

  • do → Task
  • Starts the call concurrently and returns a Task handle.
  • Example: var t = do fetch(url)

  • await(task: Task) → any

  • Waits for the task to finish and returns the call’s result (or None).
  • Example: var data = await(t)

  • sleep(ms: int) → None

  • Suspends the current task for ms milliseconds (must be non-negative).
  • Example: sleep(100)

Notes: - Errors are returned by your function in the normal way (e.g., tuples (value, error)), and await() simply yields that same return value. There is no separate “rejection” channel. - Task inspection prints whether a task is pending or done with its result.


Patterns

1) Fan-out / fan-in

Spawn many tasks, then gather results:

Fan-out / Fan-in
import fmt

function square(n int) int {
    sleep(30) // simulate work
    return n * n
}

var nums = [1, 2, 3, 4, 5]
var tasks = []
for n in nums { tasks = append(tasks, do square(n)) }

var results = []
for t in tasks { results = append(results, await(t)) }

fmt.Printf("squares: %v\n", results)  // [1, 4, 9, 16, 25]

2) Parallel map helper

Parallel Map Helper
function pmap(values array, f function(any) any) array {
    var tasks = []
    for v in values { tasks = append(tasks, do f(v)) }

    var out = []
    for t in tasks { out = append(out, await(t)) }
    return out
}

function inc(x int) int { return x + 1 }

var res = pmap([1,2,3,4], inc)
// res = [2,3,4,5]

3) Handling errors with tuples

If your function returns (value, error), just await and handle the tuple:

Handling Errors with Tuples
import fmt

function fetch(id int) (string, error) {
    if id < 0 { return ("", errors.New("bad id")) }
    sleep(20)
    return (fmt.Sprintf("item-%d", id), None)
}

var t = do fetch(42)
var item, err = await(t)
if err != None { fmt.Printf("error: %v\n", err) }
else { fmt.Printf("ok: %s\n", item) }

4) Staggered completion

Tasks can complete in any order; await determines when you observe results:

Staggered Completion
1
2
3
4
5
6
var t1 = do work("A", 200)
var t2 = do work("B", 20)

// If you await t1 first, you’ll block until A is done
var r1 = await(t1)
var r2 = await(t2)

Scheduler API

Harneet includes a small, developer-friendly scheduler API for orchestrating tasks and handling timeouts and cancellation. It is designed to be type-safe and never swallow errors.

Overview

  • TaskGroup: group and operate on multiple tasks together
  • Methods: add(task), addAll(array), awaitAll() -> array, awaitAny() -> (index int, result any), len() -> int, clear(), tasks() -> array
  • Timeouts: awaitWithTimeout(task, ms) -> (result any, error)
  • Cancellation: cooperative token primitives
  • newCancelToken() -> CancelToken
  • cancel(token)
  • isCancelled(token) -> bool
  • sleepUntil(ms, token) -> (None, error)

Examples

Scheduler API Examples
import fmt

function work(name string, ms int) string {
    sleep(ms)
    return name
}

// 1) Grouping tasks
var g = newGroup()
var t1 = do work("A", 120)
var t2 = do work("B", 40)
g.add(t1).add(t2)

var idx, first = g.awaitAny()     // returns (index, result)
fmt.Printf("first: %d %v\n", idx, first)

var all = g.awaitAll()            // returns [result1, result2]
fmt.Println(all)

// 2) Timeout awaiting a task
var slow = do work("SLOW", 200)
var value, err = awaitWithTimeout(slow, 75)
if err != None { fmt.Printf("timeout: %v\n", err) }
else { fmt.Printf("value: %v\n", value) }

// 3) Cancellation
function cancellable(name string, ms int, token any) (string, error) {
    var _, e = sleepUntil(ms, token)
    if e != None { return ("", e) }
    return (name, None)
}

var tok = newCancelToken()
var tc = do cancellable("X", 200, tok)
cancel(tok)
var res, cerr = await(tc)
if cerr != None { fmt.Printf("cancelled: %v\n", cerr) }

Design notes

  • Concurrency is implemented via lightweight tasks; do is analogous to spawning a goroutine.
  • await() acts like a join on a single task and returns the task’s result.
  • sleep() blocks only the current task, not the whole program.
  • Built-in timeouts and cancellation are available:
  • awaitWithTimeout(task, ms) produces (result, error) and never swallows errors.
  • newCancelToken() + cancel() enable cooperative cancellation; pair with sleepUntil(ms, token).

See also

  • Example file: examples/concurrency/do_await_sleep_test.ha
  • Control flow: site/docs/general/control_flow.md