[tracking] browser WASM support
yoshuawuyts opened this issue ยท 19 comments
It'd be cool if async-std
worked in the browser out of the box. Not everything can be supported, but we can support a smaller subset.
Tasks
- no longer fail compilation under the
--target wasm32-unknown-unknown
target - make
task::spawn
work (usewasm_bindgen_futures::spawn_local
) - expose
stream
- expose
task::sleep
- patch
async-attributes
to allowasync fn main
to work
References
Should we introduce async_std::os::wasm::task::spawn_local
that calls wasm_bindgen_futures::spawn_local
?
Or should we perhaps have async_std::task::spawn_local
that doesn't have the Send
bound and works on all platforms? On wasm it would call wasm_bindgen_futures::spawn_local
and on other platforms it could do something else.
What if every thread was technically a worker thread onto which local tasks can be spawned? We could have thread-local task queues into which spawn_local
pushes tasks. Regular worker threads would run tasks both from their regular queues and from those local queues, while other threads outside the runtime would run tasks from their local queues only when an invocation of async_std::task::block_on
is idle.
Just some ideas...
(EDIT: I said something wrong regarding wasm-bindgen-futures, they recently upgraded to stable futures)
One challenge is that futures_timer
will panic at runtime, because it calls std::time::Instant::now()
, which panics in WASM.
Can we replace futures-timer
with something else on wasm? Can we perhaps spawn a JS promise that completes after some number of seconds and then await that future?
Also, since Instant::now()
panics on wasm, is it possible to get the current time on by invoking a JS function?
Can we replace futures-timer with something else on wasm? Can we perhaps spawn a JS promise that completes after some number of seconds and then await that future?
Yes, that should be possible. Example (with old futures): https://github.com/tomaka/wasm-timer/blob/fd76bd42824fcee5232959df3f8a15cb58e33c46/src/wasm.rs#L209-L278
Also, since Instant::now() panics on wasm, is it possible to get the current time on by invoking a JS function?
Yes. one can use performance.now().
Example: https://github.com/tomaka/wasm-timer/blob/366b8003189487d1468dd1b65be8523fb4ae4245/src/wasm.rs#L57-L64
The problem is that it is clearly a hack to assume that wasm32-unknown-unknown
means "browser". As its name says, it's "unknown". I don't have a solution to that.
What if every thread was technically a worker thread onto which local tasks can be spawned? We could have thread-local task queues into which spawn_local pushes tasks.
@davidbarsky mentioned something like this earlier; that's very interesting!
@tomaka I was thinking of sidestepping futures-timer
entirely and creating async bindings to the timing facilities directly. It seems like that would help with keeping bundle sizes down also.
Perhaps it would actually make sense to add a time
submodule for WASM specifically so things like time::Instant::now()
actually works? I think having that would actually be really valuable, and we could probably lean heavily on the work you've already done.
The problem is that it is clearly a hack to assume that wasm32-unknown-unknown means "browser".
I've talked with the wasm team about this, and they recommended to do feature detection for wasm-bindgen
which is always passed (I believe). Afaik that should be the most reliable solution right now.
they recommended to do feature detection for wasm-bindgen which is always passed (I believe)
I don't understand what you mean by that?
@tomaka feature flag it as:
#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
as recommended by the wasm team. Not 100%, but better than just target_arch
(:
So we would need three code paths:
- The regular one (Unix, Windows)
- The one with
wasm32
+wasm-bindgen
that invokes JS functions. - The one with only
wasm32
that returns an error/panics all the time?
@tomaka yeah that's about it for now. Eventually we may want to support WASI too, but async support doesn't exist yet. We should engage the team more closely on this though; it's been a while since we last talked about it in-depth.
Related project: https://github.com/richardanaya/asynctimer
The core work of this happened in #757 and will be available in 1.6
@yoshuawuyts You have checked task::spawn
, any idea on why it cannot be found when I run:
cargo check \
--target wasm32-unknown-unknown
It shows:
error[E0425]: cannot find function `spawn` in module `task`
--> src/effector.rs:31:25
|
31 | let fut = task::spawn(async move {
| ^^^^^ not found in `task`
|
help: consider importing this function
|
1 | use std::thread::spawn;
|
error: aborting due to previous error
For more information about this error, try `rustc --explain E0425`.
error: could not compile `casbin`.
To learn more, run the command again with --verbose.
EDIT:
change to spawn_local
works
i've got this error when running cargo check --target wasm32-unknown-unknown
:
failed to resolve: use of undeclared type or module `futures_timer`
--> /Users/admin/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.6.2/src/utils.rs:84:29
|
84 | pub(crate) struct Timer(futures_timer::Delay);
| ^^^^^^^^^^^^^ use of undeclared type or module `futures_timer`
@sergeyt which vesion was that?
@dignifiedquire It says 1.6.2 :)
src/github.com-1ecc6299db9ec823/async-std-1.6.2/src/utils.rs:84:29
reading is hard :(
i've got this error when running
cargo check --target wasm32-unknown-unknown
:failed to resolve: use of undeclared type or module `futures_timer` --> /Users/admin/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.6.2/src/utils.rs:84:29 | 84 | pub(crate) struct Timer(futures_timer::Delay); | ^^^^^^^^^^^^^ use of undeclared type or module `futures_timer`
I think you'll have to add the unstable
feature to make this work
What are the next steps for this issue?
I am specifically interested in a WASM-friendly sleep. It looks like there is a working example of this from 3 years ago from the comment above:
https://github.com/richardanaya/asynctimer
Any progress since then?