WebWorkerTaskExecutor
A task executor that runs tasks on Web Worker threads.
- iOS
- 18.0+
- macOS
- 15.0+
- tvOS
- 18.0+
- visionOS
- 2.0+
- watchOS
- 11.0+
final class WebWorkerTaskExecutor
The WebWorkerTaskExecutor
provides a way to execute Swift tasks in parallel across multiple Web Worker threads, enabling true multi-threaded execution in WebAssembly environments. This allows CPU-intensive tasks to be offloaded from the main thread, keeping the user interface responsive.
Multithreading Model
Each task submitted to the executor runs on one of the available worker threads. By default, child tasks created within a worker thread continue to run on the same worker thread, maintaining thread locality and avoiding excessive context switching.
Object Sharing Between Threads
When working with JavaScript objects across threads, you must use the JSSending
API to explicitly transfer or clone objects:
// Create and transfer an object to a worker thread
let buffer = JSObject.global.ArrayBuffer.function!.new(1024).object!
let transferring = JSSending.transfer(buffer)
let task = Task(executorPreference: executor) {
// Receive the transferred buffer in the worker
let workerBuffer = try await transferring.receive()
// Use the buffer in the worker thread
}
Prerequisites
This task executor is designed to work with wasi-threads but it requires the following single extension: The wasi-threads implementation should listen to the message
event from spawned Web Workers, and forward the message to the main thread by calling _swjs_enqueue_main_job_from_worker
.
Basic Usage
// Create an executor with 4 worker threads
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 4)
defer { executor.terminate() }
// Execute a task on a worker thread
let task = Task(executorPreference: executor) {
// This runs on a worker thread
return performHeavyComputation()
}
let result = await task.value
// Run a block on a worker thread
await withTaskExecutorPreference(executor) {
// This entire block runs on a worker thread
performHeavyComputation()
}
// Execute multiple tasks in parallel
await withTaskGroup(of: Int.self) { group in
for i in 0..<10 {
group.addTask(executorPreference: executor) {
// Each task runs on a worker thread
return fibonacci(i)
}
}
for await result in group {
// Process results as they complete
}
}
Known limitations
Currently, the Cooperative Global Executor of Swift runtime has a bug around main executor detection. The issue leads to ignoring the @MainActor
attribute, which is supposed to run tasks on the main thread, when this web worker executor is preferred.
func run(executor: WebWorkerTaskExecutor) async {
await withTaskExecutorPreference(executor) {
// This block runs on the Web Worker thread.
await MainActor.run {
// This block should run on the main thread, but it runs on
// the Web Worker thread.
}
}
// Back to the main thread.
}