How to use Isolate in Dart, from spawning a new one, sending message, getting result and killing it. Find out on this tutorial.
Dart is a single-threaded language. But it doesn't mean we can't run code in parallel. Dart has Isolate which allows us to run multiple things in parallel.
Spawn an Isolate
Below is Dart's static method for spawning an Isolate which is available if you've added import 'dart:isolate';
external static Future spawn(
void entryPoint(T message), T message,
{bool paused: false,
bool errorsAreFatal,
SendPort onExit,
SendPort onError});
It only has two required parameters entryPoint
and message. The entryPoint
is a top-level function or a static method that has one parameter. It can't be an instance method or a value of function expressions.
The message will be passed to the entryPoint
function, so it's is required that the message and the function parameter has the same type.
Name | Type | Description |
---|---|---|
paused |
bool |
If true, the isolate will start up in a paused state. |
errorsAreFatal |
bool |
If true, uncaught errors will terminate the isolate. |
onExit |
SendPort |
Requests an exit message on [responsePort] when the isolate terminates. |
onError |
SendPort |
Requests that uncaught errors of the isolate are sent back to [port]. |
Kill an Isolate
After an isolate has finished its task, we can request to terminate it. Isolate has kill
method.
external void kill({int priority: beforeNextEvent});
Optionally, you can pass an argument priority
whose value if provided must be immediate
or beforeNextEvent
event. If the value is immediate
, the isolate shuts down as soon as possible. If the value is beforeNextEvent
, it will be scheduled to shutdown the next time control returns to the event loop.
Example 1
In this first example, we spawn some isolates with String message. The entryPoint
function (runSomething
) prints the message first, then call an API and print the response. If you run the script, you'll see that the isolates run in parallel (the argument on all isolates will be printed first before any isolate gets the response).
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
List<Isolate> isolates;
void start() async {
isolates = new List();
isolates.add(await Isolate.spawn(runSomething, 'first'));
isolates.add(await Isolate.spawn(runSomething, 'second'));
isolates.add(await Isolate.spawn(runSomething, 'third'));
}
void runSomething(String arg) async {
print('arg: $arg');
var request = await HttpClient().getUrl(Uri.parse('https://swapi.co/api/people/1'));
var response = await request.close();
await for (var contents in response.transform(Utf8Decoder())) {
print(contents);
}
}
void stop() {
for (Isolate i in isolates) {
if (i != null) {
i.kill(priority: Isolate.immediate);
i = null;
print('Killed');
}
}
}
void main() async {
await start();
print('Press enter to exit');
await stdin.first;
stop();
exit(0);
}
Example 2
In the second example, we want to collect the result of each isolate. To do so, we can create an instance of ReceivePort
. It has sendPort
property of type SendPort
, which allows messages to be sent to the receive port. The receive port needs to listen for data using listen
method whose parameter is a function. To send a message to the receive port, use send
method of SendPort
.
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
List<Isolate> isolates;
void start() async {
isolates = new List();
ReceivePort receivePort= ReceivePort();
isolates.add(await Isolate.spawn(runSomething, receivePort.sendPort));
isolates.add(await Isolate.spawn(runSomething, receivePort.sendPort));
isolates.add(await Isolate.spawn(runSomething, receivePort.sendPort));
receivePort.listen((data) {
print('Data: $data');
});
}
void runSomething(SendPort sendPort) async {
var request = await HttpClient().getUrl(Uri.parse('https://swapi.co/api/people/1'));
var response = await request.close();
await for (var contents in response.transform(Utf8Decoder())) {
sendPort.send(contents);
}
}
void stop() {
for (Isolate i in isolates) {
if (i != null) {
i.kill(priority: Isolate.immediate);
i = null;
print('Killed');
}
}
}
void main() async {
await start();
print('Press enter to exit');
await stdin.first;
stop();
exit(0);
}
Example 3
The third example is a combination of the first and second examples. We want to both send message to the created isolates and listen to the response of the isolates. The spawn
method only has one parameter for message but it can be any type. So, we can create a custom class that contains both SendPort
and the message, as exemplified below.
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
List<Isolate> isolates;
class CustomObject {
String message;
SendPort sendPort;
CustomObject(this.message, this.sendPort);
}
void start() async {
isolates = new List();
ReceivePort receivePort= ReceivePort();
CustomObject object1 = new CustomObject('1', receivePort.sendPort);
CustomObject object2 = new CustomObject('2', receivePort.sendPort);
CustomObject object3 = new CustomObject('3', receivePort.sendPort);
isolates.add(await Isolate.spawn(runSomething, object1));
isolates.add(await Isolate.spawn(runSomething, object2));
isolates.add(await Isolate.spawn(runSomething, object3));
receivePort.listen((data) {
print('Data: $data');
});
}
void runSomething(CustomObject object) async {
print('https://swapi.co/api/people/${object.message}');
var request = await HttpClient().getUrl(Uri.parse('https://swapi.co/api/people/${object.message}'));
var response = await request.close();
await for (var contents in response.transform(Utf8Decoder())) {
object.sendPort.send(contents);
}
}
void stop() {
for (Isolate i in isolates) {
if (i != null) {
i.kill(priority: Isolate.immediate);
i = null;
print('Killed');
}
}
}
void main() async {
await start();
print('Press enter to exit');
await stdin.first;
stop();
exit(0);
}