Skip to main content

async_retry/
async_retry.rs

1//! Async retry with no runtime: the sleep future is user-provided, and a tiny
2//! in-file executor drives the result to completion.
3//!
4//! Run with: `cargo run -p reliakit-retry --example async_retry`
5//!
6//! `reliakit-retry` does not depend on Tokio, async-std, or `futures`. Under a
7//! real runtime you would replace `block_on` with your executor and the sleep
8//! future with your runtime's async timer.
9
10use core::future::Future;
11use core::task::{Context, Poll, Waker};
12use core::time::Duration;
13
14use reliakit_retry::{retry_async, Backoff, RetryError, RetryPolicy};
15
16/// Polls a future to completion on the current thread. The futures here are
17/// always immediately ready, so this never spins.
18fn block_on<F: Future>(future: F) -> F::Output {
19    let waker = Waker::noop();
20    let mut cx = Context::from_waker(waker);
21    let mut future = core::pin::pin!(future);
22    loop {
23        if let Poll::Ready(value) = future.as_mut().poll(&mut cx) {
24            return value;
25        }
26    }
27}
28
29fn main() {
30    let policy = RetryPolicy::new(4, Backoff::constant(Duration::from_millis(20)))
31        .expect("max_attempts is non-zero");
32
33    let mut attempt = 0;
34    let result: Result<u32, RetryError<&str>> = block_on(retry_async(
35        &policy,
36        || {
37            attempt += 1;
38            let outcome = if attempt < 3 {
39                Err("temporary")
40            } else {
41                Ok(200)
42            };
43            async move { outcome }
44        },
45        |_error| true,
46        |delay| async move {
47            // Your runtime's async sleep goes here. This example resolves
48            // immediately; the sleep future is entirely user-provided.
49            let _ = delay;
50        },
51    ));
52
53    println!("async result after {attempt} attempt(s): {result:?}");
54}