isox 0.2.0 isox: ^0.2.0 copied to clipboard
Run your Isolates with bidirectional communication. Isox provides a simple request/reply pattern to communicate with your Isolates.
Isox #
Run your Isolates with bidirectional communication. Isox provides a simple request/reply pattern to communicate with your Isolates.
Usage #
Full example #
import 'package:isox/isox.dart';
void main() async {
// Start the Isox instance with the isolate.
final instance = await Isox.start(_init);
// Run the add command with 10 as input.
await instance.run(addCommand, 10);
// Close the Isox instance and the corresponding isolate.
instance.close();
}
int _init(IsoxConfig config) {
// Add the add command to the config.
config.command(addCommand);
// Return the initial state on this function.
return 0;
}
// Define an add command with the top-level runner function.
// The name ("add" in this case) must be unique!
const addCommand = IsoxCommand('add', _exec);
Future<int> _exec(int input, int state) async => state = (state + input);
Initialization #
First you need to define the initializer top-level function:
int _init(IsoxConfig config) {
/// ...
}
This function returns the initial state within the Isolate. Through the config you can define various things which are necessary to interact with the Isolate later. Note: This function MUST be top-level, otherwise it will fail to initialize.
Now you need to start the actual Isox instance:
void main() async {
final isox = await Isox.start(_init);
}
This requires the previously defined initializer function. This will return the actual IsoxInstance as a future.
Commands #
As this library provides an abstraction for the bidirectional communication between the main process, and the isolate, you need to define your commands within the Isolate. A command can accept a single input parameter and return a single value back to the main process.
The easiest way to define a command is like this:
const addCommand = IsoxCommand('add', _exec);
/// The actual function which will be executed on call.
/// This provides the input parameter and the current state.
Future<int> _exec(int input, int state) async => state = (state + input);
Note: The name of the command ("add" in this case) MUST be unique!
If you have a command which returns void
, the main process won't wait for completion of the command runner. You can
override this behavior by passing the hasResponseOverride
parameter to the IsoxCommand constructor:
const addCommand = IsoxCommand('add', _exec, hasResponseOverride: true);
Registering commands #
The Isolate process needs to be configured to accept a command. This can be done in the initializer function from the Initialization section.
Here's an example:
int _init(IsoxConfig config) {
// Add the previously defined add command to the config.
config.command(addCommand);
// ...
}
Note: An exception will be thrown if you try to register to command twice or register a command with the same name!
Executing commands #
Assume we've already initialized an IsoxInstance and the addCommand
is registered from the previous section. Through
the IsoxInstance
you can run any registered command on the Isolate process.
Example:
void main() async {
// ...
// Will run the addCommand with '10' as input.
final response = await instance.run(addCommand, 10);
}
The output will be returned as a future from the run
method.
State #
As you can keep the Isox instance alive, you may need to persist various values through multiple command calls.
You can provide an initial state through the initializer function like this:
int _init(IsoxConfig config) {
// Provide 0 as initial state.
return 0;
}
The state can be access on each command like this:
const addCommand = IsoxCommand('add', _exec);
Future<int> _exec(int input, int state) async {
// Second parameter provides the state.
// ...
}
Features and bugs #
Please file feature requests and bugs at the issue tracker.