I might found a reliably debug-able way to develop Dart web application with Web Worker (which is written in Dart in the same project).
This is a continued adventure from my Web Worker + Dart. This story is about how I work around the caveats. To recall. The first caveat is unable to develop the web application with
webdev serve , because worker will not start-up correctly in
dartdevc. Second caveat is force to use different method to convert the incoming data (in the worker, from the worker).
Cleaner method to talk with Worker — via Stream
The first thing I did when I revisit Web Worker with Dart is to refactor my previous code to be “Dart”ier. Communication with Worker is simple and clean, you post it message one by one, you listen to its message one by one. This is how it looks: An app communicate to a service inside a Web Worker.
This translate very well to
Stream. I created a class, calling it
Dorker (I am bad at naming), which has 2
StreamController that provide a
Stream for event listening and a
Sink to pass in message.
Next, I create 2 specialized classes,
DorkerBoss, which extend
DorkerWorker will encapsulate the communication with a Web Worker,
DorkerBoss will encapsulate the communication inside a Web Worker. Now: same App and service, now they talk to dorker.
Instead of using the
Worker directly in the main, we will communicate with
DorkerWorker. Vice versa, instead of calling raw JS interoped functions inside the Web Worker, we use the
Such extra layers?!
Before I proceed to tell how these 2 extra layers going to solve my initial problem, let discuss the other goods they bring.
- This decoupled the App and the service from the platform/native API. They listen to
Streamand add to
Sink, very Dart.
- Great for testing, just mock the
- Is the App talking to a Web Worker or remote service? Is the service behind a worker or still in the main thread? They don’t need to know.
From all these benefits, surely you might see my solution to use
dartdevc, is to *not* use the Web Worker
The irony… Anyway, this is my only solution to the first caveat mentioned earlier. I am a lone coder tackling a large web application development. I prefer all codes are within range (meaning, they are all in the same project) and fast turn around time (meaning, I don’t hop around different project changing 2 lines to fix a thing).
Docker and discipline, we should have decent amount of confident to decide: with or without Web Worker in between, the application should behave exactly the same, just maybe faster or slower.
The trick is easy. Instead of using
DockerBoss, we use 2 base
Dorker and cross link them. Using a Dart’s named constructor:
dartdevc Mode: App and service both in main thread.
To pull this off, we need different logic between
Use build.yaml to alternate the type of Dorker
build.yaml will let us alter the build process, a.k.a
webdev serve -r. There is not much alteration we need, in fact, just one.
We pass in an environment variable,
USE_WORKER. Remember to add
-fast-startup and other good stuff for release build when you’re ready for production.
Some where in our main application, we add logic to switch between 2 modes.
Code in main is basically done above, for Web Worker script, it should be like below:
Personally, I like how lean the Worker code is, in Dart.
Keep it simple: pass only String
This is more akin to a compromise rather than deliberate choice.
DorkerBoss do not enforce this, but I strongly recommend to pass only String. Reasons/Scenario/Exemption:
- Dart object is opaque to js, and anything passing through Worker is passing through with js. We can opt for interop js object like what I did in previous post, but that require different handling in different context.
- Even we agreed on json.encoded all objects to string, but thinking to send them over in a list (which is supported in some dart:html native function). This is not the case for Worker.postMessage. I believe this is a bug, but I’ve yet free enough to create a repro and submit the bug.
- Exemption: pass around a js native object, e.g,
Dorker, a temporary bridge and a good practice
It is all started when I really wanted to use Web Worker but
webdev serve just not let it. I’m pretty sure this will be look at and resolve by the Dart team soon. Mean time,
Dorker can bridge that gap.
Dorker turns out to be a good practice on its own. Writing a module which only takes message and pass on message via a
Dorker , and now we can put this module anywhere it seems fit. In a Worker? Just simply a composition? A serverless function out in the cloud?
Please, if you have the time, check out the GitHub project. Play with it. Or leave a comment and lets discuss about this.
Originally published at https://yuan-kuan.gitlab.io on December 15, 2018.