easy_isolate 1.2.1 easy_isolate: ^1.2.1 copied to clipboard
An abstraction of the isolate providing an easy way to work with different threads
Easy Isolate #
An abstraction of isolates providing an easy way to work with different threads
Features #
How it works #
Worker #
A worker is responsible for 1 new thread (isolate). Everything you need to do to start a new thread is initiate
a new worker and call the init
function providing 2 parameters, a MainMessageHandler and an IsolateMessageHandler.
void main() async {
final worker = Worker();
await worker.init(mainHandler, isolateHandler);
}
void mainHandler(dynamic data, SendPort isolateSendPort) {
}
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
}
The mainHandler
will receive messages coming from the isolate, and the isolateHandler
the messages coming from the
main thread.
Important note: Since you are working with different threads remember that they don't share the same space of memory, which means that all instances and states present on the main thread are not available on the new threads.
Because of these particularities, the
isolateHandler
should be a top-level function or a static method.
You can use the worker initialMessage
parameter to start the isolate sending an initial message. When the worker be
ready, the initial message will be sent to the isolate and received in the isolateHandler.
...
await worker.init(mainHandler, isolateHandler, initialMessage: 'firstMessage');
}
...
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
// Event 'firstMessage' received when the worker is ready
}
Now that you have the worker ready, and with that a new thread running, the next step is understanding how to communicate between them.
Sending messages to the isolate/thread
There are two ways to communicate with the isolate, the first one is using the method available in the worker
worker.sendMessage(null)
, and the other one is using the SendPort inside the mainHandler
. Let's suppose that you
are running more than one worker in your application and want to handle all the incoming messages from the isolates
to the main using only one handler. For this case, the SendPort inside the mainHandler
is always related to the
isolate that is sending the message.
Example using the sendMessage:
void main() async {
final worker = Worker();
await worker.init(mainHandler, isolateHandler);
worker.sendMessage(null);
}
...
Example using the mainHandler SendPort:
...
void mainHandler(dynamic data, SendPort isolateSendPort) {
isolateSendPort.send(null);
}
...
Sending messages to the main thread
There is only one way to communicate with the main thread through the isolate, using the main SendPort available in the
isolateHandler
.
...
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
mainSendPort.send(null);
}
Good to know: Only primitive types can be sent on the messages, but you can send class objects between them containing the primitive values. Example: You can instantiate a Car class inside the isolate and send it to the main thread, the object will be copied to another instance with all the primitive values intact.
Now that you know how to start a worker and send messages, this plugin provides more two handlers on the init function,
the errorHandler
and exitHandler
.
The errorHandler
will be called on two occasions, the first one is when any
unhandled exception being thrown on the isolate sending the exception stacktrace. The other one is using the
SendErrorFunction
inside the isolateHandler, can be used to send the errors manually without necessarily having an
exception.
Example using the errorHandler
:
void main() async {
final worker = Worker();
await worker.init(mainHandler, isolateHandler, errorHandler);
worker.sendMessage(null);
}
void errorHandler(dynamic data) {
// Handle the error here
}
...
Example sending messages manually:
...
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
onSendError('Error');
}
The exitHandler
is called when the worker is disposed or the isolate is closing.
Example:
void main() async {
final worker = Worker();
await worker.init(mainHandler, isolateHandler, errorHandler, exitHandler);
worker.sendMessage(null);
}
void exitHandler(dynamic data) {
// Handle the exit here
}
...
Important note: The isolate automatically closes when unhandled exceptions happens inside the isolate. One way to prevent this is using the
runGuardedZone
or wrapping the operations with try catchs.
Congratulation, now you are ready to use the worker in your project.
Parallel #
The Parallel class provides a set of ready to use methods including:
Run
Executes a function in a different thread.
The run
method will run the handler
function provided in a separated thread, returning a value or not. There is
also an entryValue
parameter that can be used inside the handler
.
Example:
Future main() async {
final result = await Parallel.run(isEven, entryValue: 1);
print(result);
}
// Top-level function (or static)
bool isEven({int? item}) {
return item != null && item % 2 == 0;
}
Map
Executes a map function in a different thread.
The map
method works like a traditional mapper, iterating through the values
, adapting with the handler
function
provided in the parameters, and returning a new List with the adapted values.
Example:
Future main() async {
final result = await Parallel.map([1, 2, 3, 4], intToStringAdapter);
print(result); // Should print all the values as a String
}
// Top-level function (or static)
String intToStringAdapter(int i) {
return i.toString();
}
Foreach
Executes a 'for in' loop function in a different thread.
The foreach
method works like a traditional 'for in' loop, iterating through the values
, running the handler
function on each value provided in the parameters.
Example:
Future main() async {
await Parallel.foreach(['test'], writeFile);
}
// Top-level function (or static)
void writeFile(String name) {
File(Directory.systemTemp.path + '/$name').createSync();
}
Example #
The example project is a simulation of a file downloader using a separated thread for each download. It will help you to understand how it works in practice. See the code here.
Contributions #
If you liked the package and would like to contribute feels free to:
- Open issues for suggestions or bugs here
- Sponsor the repo using the button on the top of the github page
- Leave your like on the pub page or a star in the github page
- Open PR's for new features
- Share with your friends