Skip to content

Concurrency

Targo's concurrency model comes from Go, not from JavaScript's async runtime.

Core Rules

  • async/await and Promise are not supported.
  • Use go(() => { ... }) for goroutines.
  • Use chan<T> for communication.
  • Use switch (chan.$select) for select-style coordination.
  • Use defer for cleanup that should run when the enclosing function returns.
  • Design the data flow and error flow explicitly. Do not assume a JavaScript async runtime model exists underneath.

Goroutines

Start a goroutine with go():

typescript
const ch = new chan<string>(1);

go(() => {
  const result = compute();
  ch.$send(result);
});

const value = ch.$recv();

Goroutines are not Promise wrappers. Only use them when concurrent execution actually provides value.

Channels

Channels are the primary communication mechanism between goroutines.

typescript
// Unbuffered channel
const ch = new chan<int>();

// Buffered channel
const buffered = new chan<string>(10);

// Send
ch.$send(42);

// Receive
const value = ch.$recv();

// Close
ch.$close();

Channel Direction

Use typed channel directions when a function only needs to send or receive:

typescript
function producer(out: chan<int>): void {
  out.$send(1);
  out.$send(2);
  out.$close();
}

Select

Use switch (chan.$select) for waiting on multiple channel operations:

typescript
const dataCh = new chan<string>(1);
const doneCh = new chan<void>(1);

switch (chan.$select) {
  case dataCh.$recv(): {
    const msg = dataCh.$recv();
    console.log(msg);
    break;
  }
  case doneCh.$recv(): {
    console.log("done");
    break;
  }
}

Defer

defer schedules a function call to run when the enclosing function returns:

typescript
function processFile(path: string): error | null {
  const [f, err] = os.Open(path);
  if (err != null) {
    return err;
  }
  defer(() => f.Close());

  // ... work with f ...
  return null;
}

Practical Patterns

NeedPrefer
Background concurrent workgoroutine + channel
Waiting on multiple communication pathsswitch (chan.$select)
Timeout-style coordinationGo-style time + channel flow
Cleanup on function exitdefer

Common Mistakes

  • Using goroutines as if they were just a Promise wrapper.
  • Designing only a success channel and forgetting the failure path.
  • Not closing channels when the sender is done.
  • Trying to translate Promise.all, race, or await one-to-one.
  • Forgetting that defer runs at function return, not at block exit.