actors 0.6.0

actors #

actors is a library that enables the use of the Actors Model in Dart.

It is a thin wrapper around Dart's Isolate (on Flutter and Dart VM) that makes them much easier to use.

Actor #

To start an Actor is very easy. You simply create a Handler implementing the logic to handle messages within the Actor's Isolate, then create an Actor using it:

class Two with Handler<int, int> {
  int handle(int n) => n * 2;
}

main() async {
  final actor = Actor(Two());
  print(await actor.send(5)); // 10
  await actor.close();
}

If your actor does not maintain internal state, it can also be created from a function:

Due to limitations of Isolate, the function must be a top-level function, i.e. not a lambda.

int two(int n) => n * 2;

main() async {
  final actor = Actor.of(two);
  print(await actor.send(5)); // 10
  await actor.close();
}

As you can see, an Actor can send a message back to the caller asynchronously.

They can also send more than one message by returning a Stream:

// A Handler that returns a Stream must use a StreamActor, not an Actor.
class StreamGenerator with Handler<int, Stream<int>> {
  @override
  Stream<int> handle(int message) {
    return Stream.fromIterable(Iterable.generate(message, (i) => i));
  }
}

main() async {
  // Create an StreamActor from a Handler that returns Stream.
  final actor = StreamActor(StreamGenerator());
  final stream = actor.send(2);
  await for (final item in stream) {
    print(item); // 0, 1
  }
  await actor.close();
}

ActorGroup #

ActorGroup allows several Actor instances to be grouped together, all based on the same Handler implementation, but executed according to one of the available strategies:

  • RoundRobin - send message to a single Actor, alternating which member of the group receives the message.
  • MultiHandler - send message to m Actors, wait for at least n successful answers.

RoundRobing is appropriate for cases where messages are CPU intensive to handle and there may be many of them.

MultiHandler is a way to achieve high reliability by duplicating effort, as not all Actors in the group may be healthy at all times. Having a few "backups" doing the same work on each message may be a good idea in case one or more of the expected receivers are likely to fail, as the system will still continue to work without issues as long as n actors remain healthy... Also, by sending the same message to several actors, the message might be received in different locations, making it much harder for it to be lost.

// create a group of 4 actors
final group = ActorGroup(Two(), size: 4);
print(await group.send(5)); // 10
group.close();

Messenger #

The Messenger mixin is implemented by Actor, ActorGroup, and also LocalMessenger, which runs its Handler in the local Isolate.

Messenger<int, int> messenger;

// a Messenger can be local
messenger = LocalMessenger(Two());
print(await messenger.send(2)); // 4

// or it can be an Actor
messenger = Actor(Two());
print(await messenger.send(3)); // 6
messenger.close();

// or an ActorGroup
messenger = ActorGroup(Two(), size: 2);
print(await messenger.send(4)); // 8
print(await messenger.send(5)); // 10
messenger.close();

This makes it possible to write code that works the same whether the message is handled locally or in another Isolate.

0.6.0 #

  • Stopped exposing Isolate from Actor. This should allow a web implementation in the future.
  • Convert remote Error into a RemoteErrorException to be able to send it back to caller.
  • Fixed close() method so that Actors actually drop all subscriptions and allow the system to die when all Actors are closed.

0.5.0 #

  • Created StreamActor to support Actors that return Streams.
  • Removed AllHandleWithNAcks GroupStrategy.
  • Created MultiHandler GroupStrategy, better implementation and more flexible than AllHandleWithNAcks.
  • Allow external implementations of GroupStrategy to be used.
  • Deprecated 'isolate' field in Actor. Will remove in the next version to allow web implementation.

0.4.0 #

  • Allow all Messenger subtypes to be created from a function.
  • Added support for GroupStrategy, so ActorGroup can have different ways to send messages to actors.
  • Created RoundRobinGroupStrategy.
  • Created AllHandleWithNAcks.
  • All Messenger sub-types are now closeable.
  • Changed return type of Messenger.send from Future to FutureOr.

0.3.0 #

  • Added ActorGroup.
  • Added LocalMessenger.

0.2.0 #

  • Lighter message representation for smaller overhead.

0.1.0 #

  • Initial version, created by Stagehand.
  • Implemented basic functionaliy of an Actor.

example/actors_example.dart

import 'dart:async';
import 'dart:core';
import 'dart:io';

import 'package:actors/actors.dart';

/// Example Handler that can be made an Actor.
class Counter with Handler<int, int> {
  int _count = 0;

  int handle(int n) => _count += n;
}

/// Expected printed output from the main function.
Iterator<String> _expectedLines = [
  '1', '2', '8', '16', '10', '12', '14', '16', '2', '3', '8', '10', '0', '1' //
].iterator;

// This function overrides Dart's "print" so we can verify the printed output
void printAndCheck(Zone self, ZoneDelegate parent, Zone zone, String line) {
  _expectedLines.moveNext();
  if (line == _expectedLines.current) {
    stdout.writeln(line);
  } else {
    throw Exception("Unexpected line: $line, not ${_expectedLines.current}");
  }
}

void main() async {
  await runZoned(() async {
    await actorExample();
    await actorGroupExample();
    await localMessengerExample();
    await streamActorExample();
  }, zoneSpecification: ZoneSpecification(print: printAndCheck));
}

Future actorExample() async {
  // Create an Actor from a Handler
  final actor = Actor(Counter());
  print(await actor.send(1)); // 1
  print(await actor.send(1)); // 2
  print(await actor.send(6)); // 8
  print(await actor.send(8)); // 16

  // Close the actor to stop its Isolate
  await actor.close();
}

int times2(int n) => n * 2;

Future actorGroupExample() async {
  // create a group of 4 actors from a simple top-level function...
  // in this example, any of the actors in the group could handle a
  // particular message.
  // Notice that lambdas cannot be provided to "of", only top-level functions.
  final group = ActorGroup.of(times2, size: 4);
  print(await group.send(5)); // 10
  print(await group.send(6)); // 12
  print(await group.send(7)); // 14
  print(await group.send(8)); // 16

  await group.close();
}

// A Handler that returns a Stream must use a StreamActor, not an Actor.
class StreamGenerator with Handler<int, Stream<int>> {
  @override
  Stream<int> handle(int message) {
    return Stream.fromIterable(Iterable.generate(message, (i) => i));
  }
}

Future streamActorExample() async {
  // Create an StreamActor from a Handler that returns Stream.
  final actor = StreamActor(StreamGenerator());
  final stream = actor.send(2);
  await for (final item in stream) {
    print(item); // 0, 1
  }
  await actor.close();
}

Future localMessengerExample() async {
  Messenger<int, int> messenger;

  // a Messenger can be local
  messenger = LocalMessenger(Counter());
  print(await messenger.send(2)); // 2
  await messenger.close();

  // or it can be an Actor
  messenger = Actor(Counter());
  print(await messenger.send(3)); // 3
  await messenger.close();

  // or an ActorGroup
  messenger = ActorGroup.of(times2, size: 2);
  print(await messenger.send(4)); // 8
  print(await messenger.send(5)); // 10
  await messenger.close();
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  actors: ^0.6.0

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:actors/actors.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
43
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
72
Learn more about scoring.

We analyzed this package on Sep 17, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.5.0
  • pana: 0.12.21

Platforms

Detected platforms: Flutter, other

Primary library: package:actors/actors.dart with components: isolate.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
Dev dependencies
pedantic ^1.0.0
test ^1.0.0