shameless copied from
Dart Web Development › Example on how to use Isolate.spawn
I hope the author doesn't mind
The spawned isolate has no idea where/how to respond to its parent.
In the parent, you could create a ReceivePort which will receive all message from child isolates.
Whenever you spawn an isolate, pass it the SendPort instance from your ReceivePort (via the message argument of Isolate.spawn).
The child isolate may/should create its own ReceivePort as well, so it can receive messages.
When instantiated, the child isolate must send its own SendPort (from its own ReceivePort) to its parent (via the parent's SendPort).
The current API is, in its own, really not helpful. But it provides all the necessary building blocks for a full-blown implementation.
You may need to wrap messages inside headers, something along these lines:
class _Request {
/// The ID of the request so the response may be associated to the request's future completer.
final Capability requestId;
/// The SendPort we must respond to, because the message could come from any isolate.
final SendPort responsePort;
/// The actual message of the request.
final dynamic message
const _Request(this.requestId, this.responsePort, this.message);
}
class _Response {
/// The ID of the request this response is meant to.
final Capability requestId;
/// Indicates if the request succeeded.
final bool success;
/// If [success] is true, holds the response message.
/// Otherwise, holds the error that occured.
final dynamic message;
const _Response.ok(this.requestId, this.message): success = true;
const _Response.error(this.requestId, this.message): success = false;
}
Every isolate could have a singleton message bus like this:
final isolateBus = new IsolateBus();
class IsolateBus {
final ReceivePort _receivePort = new ReceivePort();
final Map<Capability, Completer> _completers = {};
IsolateBus() {
_receivePort.listen(_handleMessage, onError: _handleError);
}
void _handleMessage(portMessage) {
if (portMessage is _Request) {
// This is a request, we should process.
// Here we send back the same message
portMessage.responsePort.send(
new _Response.ok(portMessage.requestId, portMessage.message));
} else if (portMessage is _Response) {
// We received a response
final completer = _completers[portMessage.requestId];
if (completer == null) {
print("Invalid request ID received.");
} else if (portMessage.success) {
completer.complete(portMessage.message);
} else {
completer.completeError(portMessage.message);
}
} else {
print("Invalid message received: $portMessage");
}
}
void _handleError(error) {
print("A ReceivePort error occured: $error");
}
Future request(SendPort port, message) {
final completer = new Completer();
final requestId = new Capability();
_completers[requestId] = completer;
port.send(new _Request(requestId, _receivePort.sendPort, message));
return completer.future;
}
}
SendPort anotherIsolatePort = ...
isolateBus.request(anotherIsolatePort, "Some message");
This is just one architectural example. You could of course roll-out your own.
This could be extended to support notifications (requests without response), streams, etc.
A global isolate registry could be needed to keep track of all SendPort instances from every isolates and eventually register them as services.