Question

There is a spawnUri(uri) function in dart:isolate, but I don't find any example. I have guessed its usage, but failed.

Suppose there are 2 files, in the first one, it will call spawnUri for the 2nd one, and communicate with it.

first.dart

import "dart:isolate";

main() {
  ReceivePort port = new ReceivePort();
  port.receive((msg, _) {
    print(msg);
    port.close();
  });
   var c = spawnUri("./second.dart");
   c.send(["Freewind", "enjoy dart"], port.toSendPort());
}

second.dart

String hello(String who, String message) {
   return "Hello, $who, $message";
}

void isolateMain(ReceivePort port) {
  port.receive((msg, reply) => reply.send(hello(msg[0], msg[1]));
}

main() {}

But this example doesn't work. I don't know what's the correct code, how to fix it?

Était-ce utile?

La solution 2

WARNING : This code is out of date.

Replace your second.dart with the following to make it work :

import "dart:isolate";

String hello(String who, String message) {
  return "Hello, $who, $message";
}

main() {
  port.receive((msg, reply) => reply.send(hello(msg[0], msg[1])));
}

Autres conseils

Here is a simple example that works with Dart 1.0.

app.dart:

import 'dart:isolate';
import 'dart:html';
import 'dart:async';

main() {
  Element output = querySelector('output');

  SendPort sendPort;

  ReceivePort receivePort = new ReceivePort();
  receivePort.listen((msg) {
    if (sendPort == null) {
      sendPort = msg;
    } else {
      output.text += 'Received from isolate: $msg\n';
    }
  });

  String workerUri;

  // Yikes, this is a hack. But is there another way?
  if (identical(1, 1.0)) {
    // we're in dart2js!
    workerUri = 'worker.dart.js';
  } else {
    // we're in the VM!
    workerUri = 'worker.dart';
  }

  int counter = 0;

  Isolate.spawnUri(Uri.parse(workerUri), [], receivePort.sendPort).then((isolate) {
    print('isolate spawned');
    new Timer.periodic(const Duration(seconds: 1), (t) {
      sendPort.send('From app: ${counter++}');
    });
  });
}

worker.dart:

import 'dart:isolate';

main(List<String> args, SendPort sendPort) {
  ReceivePort receivePort = new ReceivePort();
  sendPort.send(receivePort.sendPort);

  receivePort.listen((msg) {
    sendPort.send('ECHO: $msg');
  });
}

Building is a two-step process:

  1. pub build
  2. dart2js -m web/worker.dart -obuild/worker.dart.js

See the complete project here: https://github.com/sethladd/dart_worker_isolates_dart2js_test

This gist: https://gist.github.com/damondouglas/8620350 provides a working (I tested it) Dart 1.5 example. An Isolate.spawn(...) example can be found there as well.

Reproducing here (adding import statements):

echo.dart:

import 'dart:isolate';
void main(List<String> args, SendPort replyTo) {
  replyTo.send(args[0]);
}

main.dart:

import 'dart:isolate';
import 'dart:async';
main() {
  var response = new ReceivePort();
  Future<Isolate> remote = Isolate.spawnUri(Uri.parse("echo.dart"), ["foo"], response.sendPort);
  remote.then((_) => response.first)
    .then((msg) { print("received: $msg"); });
}

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top