Why is poll only executed one times?

Hi, i have written an asynchrous code with wasm_bindgen, i used spawn_local to run a future, i found that the poll function was only executed one times, the example code likes below:

use std::{future::Future, task::Poll};

use wasm_bindgen_futures::spawn_local;

struct A {
    count: i32,
}

impl Future for A {
    type Output = i32;

    fn poll(
        self: std::pin::Pin<&mut Self>,
        _cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        log::info!("poll one times.");
        if self.count < 10 {
            self.get_mut().count = self.count + 1;
            Poll::Pending
        } else {
            Poll::Ready(self.count)
        }
    }
}

pub fn start() {
    spawn_local(async {
        let output = A { count: 0 }.await;
        log::info!("output: {}", output);
    });
}

playground
From the browser's console, the poll function was only executed one times, and the future's status was always pending.
image

If a future isn't ready, it's expected to use the waker provided by the context (_cx in your code) to wake itself up when it is ready.

The async book has an example.

1 Like

I think your future needs to wake to make process even if it returns pending. As I understand it, returning Poll::Pending lets the executor know it's not ready, but the executor doesn't know to poll again unless the future tells it to do so.

As others have said, despite the name, async doesn't actually poll, at least the way that term is normally used.

Instead, by returning Pending, you are saying "I don't have a value yet, I'm going to call wake on the waker some time later when I do."

The async machinery (including some parent future) can still decide to poll you if you haven't called wake yet, hence being called poll. This is because it might be more efficient to not bother tracking which of several futures called poll, or to coalesce multiple changes at once, etc.

Thank you all! I misunderstanded the usage of Future . I modified my code, and it worked.

use std::{future::Future, rc::Rc, task::Poll};

use wasm_bindgen::{prelude::Closure, JsCast};
use wasm_bindgen_futures::spawn_local;

struct A {
    count: i32,
}

impl Future for A {
    type Output = i32;

    fn poll(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Self::Output> {
        log::info!("poll one times.");
        if self.count < 10 {
            self.get_mut().count = self.count + 1;
            // wait every second
            let wait_fn = {
                let waker = Rc::new(cx.waker().clone());
                Closure::wrap(Box::new(move || {
                    waker.as_ref().clone().wake();
                }) as Box<dyn Fn()>)
            };
            let _ = web_sys::window()
                .unwrap()
                .set_timeout_with_callback_and_timeout_and_arguments_0(
                    wait_fn.as_ref().unchecked_ref(),
                    1000,
                );
            wait_fn.forget();
            Poll::Pending
        } else {
            Poll::Ready(self.count)
        }
    }
}

pub fn start() {
    spawn_local(async {
        let output = A { count: 0 }.await;
        log::info!("output: {}", output);
    });
}

image