Skip to content

Added Vite bundler support, Channels & Async functions#5

Merged
paberr merged 6 commits intopaberr:mainfrom
Roba1993:vite-support
Jan 29, 2026
Merged

Added Vite bundler support, Channels & Async functions#5
paberr merged 6 commits intopaberr:mainfrom
Roba1993:vite-support

Conversation

@Roba1993
Copy link
Contributor

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. :)

Cargo.toml Outdated
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
Contributor 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.

Cargo.toml Outdated
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
Contributor 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
Contributor Author

Choose a reason for hiding this comment

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

Yeah makes sense!

src/func.rs Outdated
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
Contributor 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
Contributor 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
@paberr
Copy link
Owner

paberr commented Jan 29, 2026

I finally found some time to come back to this. Since it has been a while, I rebased and cleaned up the PR a bit myself.
Most notably, I reverted the serialization from pot back to postcard as I had some time to think about it.

I agree, pot handles complex types like Rc<T> and cyclic structures, but comes with significant performance differences:

Taken from: https://github.com/djkoloski/rust_serialization_benchmark

Metric postcard pot
Serialize ~444.72 µs ~38.260 ms
Deserialize ~1.9938 ms ~77.356 ms
Output size ~367.49 KB ~10.12 MB

For the typical WebWorker use case (passing Vec<T>, structs, primitives), postcard is the better default.
I thought about adding both, but that becomes a problem with maintenance of the library and incompatibility of formats.
Additionally, users with complex serialization needs can still use run_bytes() or build a wrapper around that.

@paberr paberr merged commit af280dd into paberr:main Jan 29, 2026
4 checks passed
@paberr
Copy link
Owner

paberr commented Jan 29, 2026

Thanks a lot for the contribution and apologies that it was taking so long! I'll also publish a new version today. :)

@paberr paberr linked an issue Jan 29, 2026 that may be closed by this pull request
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.

Vite Bundler Support

2 participants