For the most part I am content to create Dart examples that run in the SDK. Command-line scripts feel lighter than their browser-based counterparts (and are certainly lighter than their JavaScript compiled counterparts). Plus, I can usually convert my scripts to run on DartPad. But I feel compelled to try my remote proxy pattern websocket implementation in the browser—not because I fear something going wrong. Rather, I am curious to explore coding to an interface in two different locations.
I create a simple
web/index.html
page to hold a remote proxy car instance. As UIs go, this barely qualifies as bare minimum: Still, it should be sufficient to test things out.
Surprisingly (at least to me) things go amiss here. I had forgotten that the
WebSocket
class in the dart:html
library differs from the one that I had been using in dart:io
. The former conforms to the standard browser websocket interface where the latter is a standard Stream
implementation. Aside from the annoyance of two different interfaces for the same object, there are some practical differences for which I have to account. In the
drive.dart
script pulled into the web page via <script>
tag, I have to add a Completer
to await the websocket being ready for use:import 'dart:async' show Completer; import 'dart:html' show WebSocket, document, query; import 'package:proxy_code/web_car.dart'; main() async { var socket = new WebSocket('ws://localhost:4040/ws'); var _c = new Completer(); socket.onOpen.listen((_){ _c.complete(); }); await _c.future; // Create proxy car with send/receive streams ProxyCar car = new ProxyCar(socket); // ... }The
web_car.dart
file with the web remote proxy implementation also needs to change slightly, listening to the onMessage
property instead of directly to the socket:class ProxyCar implements AsyncAuto { // ... ProxyCar(this._socket) { _socket.onMessage.listen((e) { _state = e.data; }); } // ... }But once those minor differences are sorted out, everything just works. Darn it.
I hook up the buttons in the page to event listeners in the Dart script:
document.query('#drive').onClick.listen((_) async {
print('Drive');
await car.drive();
updateState(car.state);
});
Clicking the button tells the proxy car to drive, which then results in an update to the state from the websocket. The result of this is included in the <pre>
tag below the buttons:And, indeed, the web socket server from the other night is seeing these messages and passing them along to the real car:
$ ./bin/server.dart [AsyncCar] received: stop [AsyncCar] received: driveIn the sum total of things, that is a good thing. I was able to take my command-line remote proxy package and use it in the browser with only minor changes. I hadn't counted on those minor changes between websocket implementations. And I had expected that I would need to move the common interface out into a separate file that could be imported into both
dart:io
and dart:html
libraries. That turned out not be necessary--thanks to Dart's beautiful libraries and creating the websockets in the client instead of the libraries.Since libraries work so well in this case, I have no doubt that they will work when the common subject interface is pulled out into its own file. I do it anyway, creating
interface.dart
with:library car; import 'dart:async' show Future; // Subject abstract class AsyncAuto implements Automobile { String get state; Future drive(); Future stop(); }I then import that into both
real_car.dart
and web_car.dart
:library car; import 'dart:async' show Future, Stream; import 'dart:html' show WebSocket; import 'interface.dart'; // Proxy Subject class ProxyCar implements AsyncAuto { // ... }That makes the type analyzer happy and positions me well should I ever decide to create the specific websockets implementations in the
dart:html
and dart:io
libraries. In other words, no matter how I decide I want to code those implementations, I have easy access to the interface to program against.Day #72
google 4207
ReplyDeletegoogle 4208
google 4209
google 4210
google 4211