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