Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Promises

A Promise is a value that will arrive in the future. It has three states: pending (not yet settled), fulfilled (resolved with a value), or rejected (failed with a reason). Once settled, a promise never changes state.

Creating Promises

(bind promise (new Promise (fn (:function resolve :function reject)
  (set-timeout (fn () (resolve 42)) 1000))))
const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve(42), 1000);
});

The constructor takes a function with two callbacks: resolve (fulfill the promise) and reject (reject it). In practice, you rarely create promises manually — most async APIs already return them.

Consuming Promises

The .then/.catch/.finally methods handle promise results:

(-> (fetch "/api/data")
  (:then (fn (:any response) (response:json)))
  (:then (fn (:any data) (process data)))
  (:catch (fn (:any error) (console:error error)))
  (:finally (fn () (cleanup))))
fetch("/api/data")
  .then(response => response.json())
  .then(data => process(data))
  .catch(error => console.error(error))
  .finally(() => cleanup());

Promise Chaining

.then returns a new promise. This is why chaining works — each .then produces a new promise that resolves when the handler’s return value resolves. Returning a plain value wraps it in a resolved promise. Returning a promise chains to that promise.

This “flattening” behaviour is the key insight: promises compose. You don’t nest them — you chain them.

In Practice

You’ll rarely write .then chains in Lykn. Async/await (next section) provides the same semantics with cleaner syntax. But understanding promises is essential because async/await is built on top of them — await consumes a promise, async produces one.