Timers & Timeouts
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()→ setsresolved=true, wakes poller - Promise destroyed early →
~TimerAdapter()callsxTimerStop(if not yet fired, checked viam_firedatomic flag) - Loop destroyed →
on_cancelcallback nullsm_handle, setsm_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