Timers & Timeouts

← Promise

When to Use

You need a delay, a timeout, or want to race a Promise against a timer.

after(ms)

Resolves after ms milliseconds. Available only on Promise<void>. Chain with .then() to run code after the delay.

xpp::EventLoop loop;
xpp::WaitScope scope(loop);

xpp::after(100)
    .then([]() { printf("100ms elapsed\n"); })
    .await();

Internally uses AdapterPromiseNode<void, TimerAdapter>. The TimerAdapter owns an xTimer handle:

  • Timer fires → callback calls m_resolver.resolve() → sets resolved=true, wakes poller
  • Promise destroyed early → ~TimerAdapter() calls xTimerStop (if not yet fired, checked via m_fired atomic flag)
  • Loop destroyed → on_cancel callback nulls m_handle, sets m_fired=true

The promise must be destroyed on the same WaitScope thread.

Timeout Pattern with race

Combine after() with race() to implement timeouts:

#include <xpp/promise_combinators.h>

xpp::EventLoop loop;
xpp::WaitScope scope(loop);

// Fetch takes 100ms, timeout is 10ms → timeout wins
int result = xpp::race(
    xpp::after(100).then([] { return 200; }),  // "fetch"
    xpp::after(10).then([] { return -1; })     // timeout
).await();
// result == -1 (timeout)

When race resolves, the losing branch is destroyed. TimerAdapter's destructor calls xTimerStop — the 100ms timer is cancelled, no callback fires after destruction.

Sequential Delays

xpp::after(10)
    .then([]() { return xpp::after(20); })  // auto-flattened
    .then([]() { printf("30ms total\n"); })
    .await();

Void Promise Chains

int counter = 0;
xpp::yield()
    .then([&]() { counter++; })
    .then([&]() { counter++; })
    .await();
// counter == 2

lazy() and yield()

// defer: wrap a sync function as a promise
int result = xpp::lazy([] { return 42; }).await();
// result == 42

// yield: immediately-resolved Promise<void>, chain entry point
int val = xpp::yield().then([] { return 1; }).await();
// val == 1