Is there any example for dart's `spawnUri(...)` in library "dart:isolate"?
Asked Answered
E

4

5

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?

Embryo answered 25/6, 2013 at 14:6 Comment(0)
T
3

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])));
}
Todo answered 25/6, 2013 at 14:39 Comment(1)
Not sure because since this answer the isolate api has been refactored.Todo
C
8

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

Coati answered 20/1, 2014 at 4:58 Comment(0)
T
3

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])));
}
Todo answered 25/6, 2013 at 14:39 Comment(1)
Not sure because since this answer the isolate api has been refactored.Todo
C
1

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"); });
}
Cortex answered 3/8, 2014 at 11:48 Comment(0)
L
1

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.

Latashalatashia answered 3/8, 2014 at 11:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.