Skip to content

Conversation

Roba1993
Copy link

To support the Vite bundler (and possible others), I figured out, that providing the wasm.js file as a path is not enough. Additionally also the direct path to the was.wasm file need to be provided. If both are delivered correctly it's working. This solves my issue #4.

The implementation simply adds a new path for the actual wasm file. Please let me know, when the naming is still a bit off.

Following my example code from my index.js file loaded with Vite:

import wasmUrl from "../pkg/webapp.js?url";
import wasmBgUrl from "../pkg/webapp_bg.wasm?url";
import init, {
  main,
  initWorkerPool,
  WorkerPoolOptions,
} from "../pkg/webapp.js";
async function run() {
  // main app init and run
  await init();
  main();

  // web worker setup
  let options = new WorkerPoolOptions();
  options.path = window.location.origin + wasmUrl;
  options.path_bg = window.location.origin + wasmBgUrl;
  options.num_workers = 4;
  initWorkerPool(options);
}
run();

This leaves one problem for now. Using the worker pool is only possible after it has been fully loaded. With this configuration the main rust code is running before the WebWorkerPool is fully initialized. Using it inside rust before will create a new default one which will fail. This is due to the actual implementation here.

.get_or_init(|| async {

Could this be changed to something which is waiting until the pool is ready?

My local workaround for this is the following ugly code:

pub async fn ww_pool() -> &'static wasmworker::pool::WebWorkerPool {
    while !wasmworker::has_worker_pool() {
        sleep(Duration::from_millis(100)).await;
    }
    wasmworker::worker_pool().await
}

@Roba1993 Roba1993 changed the title Support of Vite bundler Added Vite bundler support, Channels & Async functions May 6, 2025
@paberr
Copy link
Owner

paberr commented Jul 24, 2025

Hi Robert,

Thanks a lot for taking this on and apologies for the delay. I had some busy months and only got back to this now.
I'll be back with more comments soon!

Copy link
Owner

@paberr paberr left a comment

Choose a reason for hiding this comment

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

I had a quick look at the changes already.
My main two questions would be:

  • What are the advantages of pot over postcard for the serialisation?
  • What is the new channel/second argument used for?

If you could provide me with a minimal example to test it with vite, that would also be useful. :)

futures = "0.3"
js-sys = { version = "0.3" }
postcard = { version = "1.0", features = ["alloc"] }
pot = "3.0"
Copy link
Owner

Choose a reason for hiding this comment

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

Any reason for switching the serialisation?

Copy link
Author

Choose a reason for hiding this comment

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

I switched, because for complex structure, like Rc and others, postcard just failed. That's why I went this way. Also pot is self-describing. Which is theoretically not needed here, but handles complex cases like above cleanly.

tokio = { version = "1.4", features = ["sync"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
log = "*"
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
log = "*"
log = "0.4"

Copy link
Author

Choose a reason for hiding this comment

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

Yeah makes sense!

[dependencies]
syn = { version = "2.0", features = ["full", "extra-traits"] }
quote = "1.0"
log = "*"
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
log = "*"
log = "0.4"

Copy link
Author

Choose a reason for hiding this comment

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

Yeah makes sense!

pub(crate) name: &'static str,
/// The original function, which can be used as a fallback.
pub(crate) func: fn(T) -> R,
pub(crate) func: fn(T, Option<Channel>) -> LocalBoxFuture<'static, R>,
Copy link
Owner

Choose a reason for hiding this comment

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

What is the channel used for?

Copy link
Author

Choose a reason for hiding this comment

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

The reason to use it, is that web-workers don't have access to the DOM and other parts of the browser. With this channel I can command the main thread to do such actions for the web-worker and return the result bag.

In my example it's something like this:

  1. Main thread is calling an async function inside web-worker
  2. WebWorker is running a scripting language, wich sometimes need to access to Dom or other actions locked away in web-worker
  3. WebWorker function uses the channel to get infos from Dom or send action to update dom
  4. Main thread is handling the channel tasks and send result to web-worker
  5. WebWorker receive results, repeats steps 2ff or finishes and returns an result to the main thread

Copy link
Author

Choose a reason for hiding this comment

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

As you can see, the focus widened a lot after I figured out the Vite support implementation :D

@paberr paberr added the enhancement New feature or request label Jul 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants