Skip to content

Conversation

@Gingeh
Copy link
Contributor

@Gingeh Gingeh commented Nov 10, 2025

Objective

  • @NthTensor suggested using their web-task crate so tasks can use the normal cancel semantics on the web.

Solution

  • Remove bevy's Task wrapper, since it's now redundant.
  • Use web_task::spawn_local(future) in single_threaded_task_pool.rs.

Testing

I ran all of the async tasks examples locally and via wasm and they behaved identically.

@alice-i-cecile alice-i-cecile added the X-Contentious There are nontrivial implications that should be thought through label Nov 10, 2025
@alice-i-cecile alice-i-cecile added S-Needs-Review Needs reviewer attention (from anyone!) to move forward C-Feature A new feature, making something new possible C-Dependencies A change to the crates that Bevy depends on O-Web Specific to web (WASM) builds D-Async Deals with asynchronous abstractions A-Tasks Tools for parallel and async work M-Release-Note Work that should be called out in the blog due to impact labels Nov 10, 2025
@github-actions
Copy link
Contributor

It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note.

Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes.

@Gingeh
Copy link
Contributor Author

Gingeh commented Nov 10, 2025

Okay I wrote a basic release note. Let me know if I should change anything.

Also @NthTensor, do you want to be added as an author? (you did most of the work anyway)

@hymm
Copy link
Contributor

hymm commented Nov 10, 2025

@NthTensor I see you took out any support for wasm multithreading. What happens if you try to compile with multithreading enabled right now? Send and Sync seem to be implemented unconditionally for the Wrapper while it's conditional in wasm-bindgen-futures. What still needs to be done to wasm-task for it to work for web multithreading in the future?

@NthTensor
Copy link
Contributor

NthTensor commented Nov 10, 2025

Thanks for submitting this. I'd just like to add some context here, for other reviewers.

Context

Bevy's executor is largely built around async-task, and the Task<T> handle type. But on web we don't use a normal rust async executor, we use the spawn_local function from wasm-bindgen-futures instead -- and this dosn't have the same semantics as async-task. The main reason "tasks are detached on the web" currently is just because that's how wasm-bindgen-futures chooses to be.

Under the hood, calling web_bindgen_futures::spawn_local(future) adds the future to a queue, and then schedules a browser microtask (via queueMicrotask) that drains the queue and polls the futures. When a future is woken, it is re-added to the queue.

This happens to be incredibly well aligned with the model of execution needed by async-task; calling async_task::spawn_local provides a Runnable and a Task<T>. The runnable needs to be queued and polled, and the task can be handed back to the caller as a handle.

So web-tasks is basically a very minor fork of wasm-bindgen-futures that uses async-task. It hands back a Task<T>, queues a Runnable, and then polls the runnable to completeion in browser microtasks. I recommend reviewers also read it over because it is very simple.

Current Status

This PR integrates web-task exactly as intended.

The only caveat is that web-task has probably has a safety hole for web-workers currently. I would be fine to merge this, and then try to fix that upstream, or vise-versa.

If reviewers would prefer, I'd be willing to transfer ownership of web-task to the foundation as well.

Copy link
Contributor

@NthTensor NthTensor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I would also recommend adding a migration guide that explains that dropping a Task is now significant on the web, and task.detach() must be called to preserve the previous behavior.

@NthTensor
Copy link
Contributor

NthTensor commented Nov 10, 2025

@hymm

What happens if you try to compile with multithreading enabled right now?

Let me see if I remember. I believe there may be an issue when sending a waker to a different thread for a !Send future. Because the queue is static (which is not shared with web-workers on wasm) and the microtask runs on the web-worker's thread, the future is always polled on the thread it is scheduled on. So if a future is woken on a thread other than the one it was first polled on, it will run on that thread. That could theoretically violate !Send requirements.

Send and Sync seem to be implemented unconditionally for the Wrapper while it's conditional in wasm-bindgen-futures. What still needs to be done to wasm-task for it to work for web multithreading in the future?

I'm not exactly sure what the next steps are here tbh. Probably porting over the more complex queue from wasm-bindgen-futures

@Gingeh
Copy link
Contributor Author

Gingeh commented Nov 11, 2025

Fixed the non-american spelling in the release note and added a migration guide

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Tasks Tools for parallel and async work C-Dependencies A change to the crates that Bevy depends on C-Feature A new feature, making something new possible D-Async Deals with asynchronous abstractions M-Release-Note Work that should be called out in the blog due to impact O-Web Specific to web (WASM) builds S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Contentious There are nontrivial implications that should be thought through

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants