isolate_manager 6.0.0+2 copy "isolate_manager: ^6.0.0+2" to clipboard
isolate_manager: ^6.0.0+2 copied to clipboard

Create long-lived isolates for single or multiple functions, with support for Web Workers (via an efficient generator) and WASM compilation.

Isolate Manager #

codecov Pub Version Pub Points Pub Downloads Pub Likes

PubStats Popularity PubStats Rank PubStats Dependents

What is Isolate Manager? #

A powerful Flutter/Dart package that simplifies concurrent programming using isolates, with cross-platform support including web and WebAssembly.

Features #

  • Flexible Isolate Management: Efficiently handle background tasks with various isolate lifecycles:

    • One-off Isolates: Ideal for single, intensive tasks like calculations, with web worker support.
    • Multi-Function Isolates: Reuse isolates for multiple functions, reducing overhead.
    • Single-Function Isolates: Dedicated isolates for continuous data processing.
  • Cross-Platform Support: Seamlessly run concurrent code across Dart VMs, Web, and WASM:

    • Web & WASM: Automatically compiles isolate functions to JavaScript Workers.
    • Fallback: Uses Future/Stream if Workers are unavailable.
  • Smart Queue Management: Optimize task execution with automatic queuing, priority tasks, and customizable strategies.

  • Type & Exception Safety: Ensure reliable data transfer and error handling across platforms using specialized types (ImNum, ImString, etc.) and custom exceptions.

  • Additional Features:

    • Custom Functions: Full control over isolate execution.
    • Progress Updates: Send intermediate results from isolates.
    • Code Generation: Automate web worker creation (without using build_runner).
    • Benchmark: Compare performance across concurrency approaches.

Setup #

Add the dependencies:

dart pub add isolate_manager
dart pub add isolate_manager_generator --dev # Only when you want to use the JS Worker generator
copied to clipboard

Functions used in isolates must be static or top-level. Add @pragma('vm:entry-point') to prevent tree-shaking:

@pragma('vm:entry-point')
int add(List<int> values) {
  return values[0] + values[1];
}
copied to clipboard

Annotations & Platform Setup #

Mobile/Desktop #

  • No additional setup required

Web #

  • Function Annotations: Annotate the methods that you want to generate to the JS Worker. Remember to run the generator after adding or modifying these annotations.

    • @isolateManagerWorker – For one-off or single-function isolates
    • @isolateManagerSharedWorker – For shared multi-function isolates
    • @isolateManagerCustomWorker – For custom isolate functions with manual control
  • Required: Functions must not depend on Flutter libraries. Only Dart primitives, Maps, and Lists are allowed or using the ImTypes.

    • Only primitive Dart types (num, String, bool, null) are directly transferable to web workers.
    • For complex data structures, use Map and List containing these primitives or the provided ImType wrappers.
  • Generate the JavaScript Workers with:

    dart run isolate_manager:generate
    
    copied to clipboard

WASM Notes #

  • When using WebAssembly, int types (including in collections) are processed as double. A built-in converter automatically fixes this, or disable with enableWasmConverter: false.

  • If the app hangs when running with flutter run -d chrome --wasm, use:

    flutter run -d chrome --wasm --web-header=Cross-Origin-Opener-Policy=same-origin --web-header=Cross-Origin-Embedder-Policy=require-corp
    
    copied to clipboard

Usage Examples #

One-off Isolate #

Use a one-off isolate with either of these methods:

@isolateManagerWorker
int fibonacciRecursive(int n) {
  if (n == 0) return 0;
  if (n == 1) return 1;
  return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
}

// Option 1: Explicit worker parameters
final fibo40 = await IsolateManager.run(
  () => fibonacciRecursive(40),
  workerName: 'fibonacciRecursive',
  workerParameter: 40,
);

// Option 2: Automatic worker mapping
final fibo40 = await IsolateManager.runFunction(fibonacciRecursive, 40);
copied to clipboard

Long-lived Multi-Function Isolates #

void main() async {
  final sharedIsolate = IsolateManager.createShared(
    concurrent: 3,
    useWorker: true,
    workerMappings: {
      addFuture: 'addFuture',
      add: 'add',
    },
  );

  sharedIsolate.stream.listen((value) {
    print('Intermediate value: $value');
  });


  final added1 = await sharedIsolate.compute(addFuture, [1.1, 2.2]);
  print('addFuture: 1.1 + 2.2 = $added1');

  final added2 = await sharedIsolate.compute(add, [1, 2]);
  print('add: 1 + 2 = $added2');

  await sharedIsolate.stop(); // Or `restart` if you want to restart
}

@isolateManagerSharedWorker
Future<double> addFuture(List<double> values) async {
  return values[0] + values[1];
}

@isolateManagerSharedWorker
int add(List<int> values) {
  return values[0] + values[1];
}
copied to clipboard

Long-lived Single Function Isolate #

main() async {
  final isolate = IsolateManager.create(
    fibonacci,
    workerName: 'fibonacci',
    concurrent: 2,
  );

  isolate.stream.listen((value) {
    print('Intermediate value: $value');
  });

  final fibo = await isolate(20);

  await isolate.stop(); // Or `restart` if you want to restart
}

@isolateManagerWorker
int fibonacci(int n) {
  if (n == 0) return 0;
  if (n == 1) return 1;
  return fibonacci(n - 1) + fibonacci(n - 2);
}
copied to clipboard

Custom Function Usage & Try-Catch #

Define a custom function with full control over events, error handling, and progress updates:

main() {
  final isolateManager = IsolateManager.createCustom(
    customIsolateFunction,
    workerName: 'customIsolateFunction',
    debugMode: true,
  );

  try {
    final fibo40 = await isolateManager.compute(40);
  } catch (e) {
    // Exception handling
  }
}

@isolateManagerCustomWorker
void customIsolateFunction(dynamic params) {
  IsolateManagerFunction.customFunction<int, int>(
    params,
    onEvent: (controller, message) {
      try {
        final result = fibonacci(message);
        controller.sendResult(result);
      } catch (err, stack) {
        controller.sendResultError(IsolateException(err, stack));
      }
      return 0;
    },
    onInit: (controller) {
      // Initialization logic here
    },
    onDispose: (controller) {
      // Cleanup actions here
    },
    autoHandleException: false,
    autoHandleResult: false,
  );
}
copied to clipboard

Advanced Features #

Queue Management #

  • Priority Tasks: Set priority: true to move critical computations to the front of the queue

  • Queue Limits: Define maxCount to limit queued tasks

  • Queue Strategies: Customize behavior when queue limit is reached:

    • UnlimitedStrategy() – No limit (default)
    • DropNewestStrategy() – Drops newest task when the maxCount is reached
    • DropOldestStrategy() – Drops oldest task when the maxCount is reached
    • RejectIncomingStrategy() – Rejects new tasks when the maxCount is reached
  • Custom Queue Strategy: Extend QueueStrategy to implement your own logic:

    class CustomStrategy<R, P> extends QueueStrategy<R, P> {
      @override
      bool continueIfMaxCountExceeded() {
        // Custom logic using `queues`, `queuesCount`, and `maxCount`
    
        return true; // Allow new tasks
        // or
        return false; // Reject new tasks
      }
    }
    
    copied to clipboard

Progress Updates #

Receive progress updates before the final result:

main() {
  final isolateManager = IsolateManager.createCustom(progressFunction);

  final result = await isolateManager.compute(100, callback: (value) {
    final data = jsonDecode(value);
    if (data.containsKey('progress')) {
      print('Progress: ${data['progress']}');
      return false; // Indicates this is a progress update
    }
    print('Final result: ${data['result']}');
    return true; // Final result received
  });
}

@isolateManagerCustomWorker
void progressFunction(dynamic params) {
  IsolateManagerFunction.customFunction<String, int>(
    params,
    onEvent: (controller, message) {
      // Send progress updates
      for (int i = 0; i < message; i++) {
        final progress = jsonEncode({'progress': i});
        controller.sendResult(progress);
      }

      // Send final result
      return jsonEncode({'result': message});
    },
  );
}
copied to clipboard

Type Safety for Web Workers #

Use helper types to ensure safe data transfer:

main() {
  // Convert native Dart objects to ImType:
  try {
    final isolate = IsolateManager.create(isolateFunction, workerName: 'isolateFunction');
    final param = ImList.wrap([1, 2, 3]);

    final result = await isolate.compute(param);
  } on UnsupportedImTypeException catch (e) {
    // Throws `UnsupportedImTypeException` when there is unsupported type
  }
}

@isolateManagerWorker
ImMap isolateFunction(ImList numbers) {
  // Decode the list into standard Dart types
  final data = numbers.unwrap!;
  final map = Map.fromEntries(
    data.map((e) => MapEntry(ImString('$e'), ImNum(e as num))),
  );
  return ImMap(map);
}

copied to clipboard

Available ImTypes (non-nullable types only):

final number = ImNum(1); // or 1.0
final string = ImString('text');
final boolean = ImBool(true);
final list = ImList(<ImType>[]);
final map = ImMap(<ImType, ImType>{});
copied to clipboard

Exception Safety for Web Workers #

Create exceptions that can be safely transferred between isolates:

main() {
  // Register the custom exception type for proper isolate communication
  IsolateManager.registerException(
    (message, stackTrace) => CustomIsolateException(message),
  );

  final isolate = IsolateManager.create(throwsCustomIsolateException);

  try {
    await isolate.compute(ImNum(0));
  } on CustomIsolateException catch (e, s) {
    print(e); // 'Custom Isolate Exception'
  }
}

class CustomIsolateException extends IsolateException {
  const CustomIsolateException(super.error);

  @override
  String get name => 'CustomIsolateException';
}

@isolateManagerWorker
ImNum throwsCustomIsolateException(ImNum number) {
  throw const CustomIsolateException('Custom Isolate Exception');
}
copied to clipboard

Generator Commands & Flags #

Ensure that the platform setup for the web is completed before running the following commands.

Generate JavaScript Workers after adding or modifying annotated functions:

dart run isolate_manager:generate
copied to clipboard

Additional options:

  • --single – Generate only single-function isolates
  • --shared – Generate only shared-function isolates
  • --in <path> (or -i <path>) – Input folder
  • --out <path> (or -o <path>) – Output folder
  • --obfuscate <level> – JS obfuscation level (0–4, default is 4)
  • --debug – Retain temporary files for debugging
  • --worker-mappings-experiment=lib/main.dart – Auto-generate worker mappings (experiment)

Additional Information #

  • Queue Info: Use queuesLength to get the current queue size
  • Startup Control: Use ensureStarted to await manual start; check isStarted to see if initialization is complete
  • Data Flow: When using converters, data flows from Main → Worker → Main → Converter → Final Result

Benchmark #

This benchmark compares the performance of recursive Fibonacci calculations using different concurrency approaches in various environments. The measurements are in microseconds and were taken on a MacBook M1 Pro 14" with 16GB RAM.

  • VM
Fibonacci Main App One Isolate Three Isolates IsolateManager.runFunction IsolateManager.run Isolate.run
30 551,928 541,882 195,646 553,949 547,982 538,820
33 2,273,956 2,268,299 816,148 2,288,071 2,282,269 2,271,376
36 9,761,067 9,669,422 3,453,328 9,643,678 9,606,443 9,648,076
  • Chrome (with Worker support, JS compiler)
Fibonacci Main App One Isolate Three Isolates IsolateManager.runFunction IsolateManager.run Isolate.run (Unsupported)
30 2,274,100 573,900 211,700 1,160,800 1,181,800 0
33 9,493,100 2,330,900 821,400 2,860,800 2,866,300 0
36 40,051,000 9,756,200 3,452,100 10,281,200 10,270,300 0
  • Chrome (with Worker support, WASM compiler)
Fibonacci Main App One Isolate Three Isolates IsolateManager.runFunction IsolateManager.run Isolate.run (Unsupported)
30 242,701 552,800 200,300 1,099,100 1,081,800 0
33 1,027,300 2,315,700 819,800 2,863,700 2,852,600 0
36 4,396,300 9,709,700 3,446,300 10,284,000 10,375,800 0

Detailed benchmark info

Contributions #

If you encounter issues or have suggestions for improvements, please open an issue or submit a pull request. If you appreciate this work, consider buying me a coffee:

BMC QR

96
likes
160
points
10.2k
downloads

Publisher

verified publisherlamnhan.dev

Weekly Downloads

2024.09.26 - 2025.04.10

Create long-lived isolates for single or multiple functions, with support for Web Workers (via an efficient generator) and WASM compilation.

Repository (GitHub)
View/report issues

Topics

#isolate #worker #concurrent #pool

Documentation

API reference

License

MIT (license)

Dependencies

path, stream_channel, web

More

Packages that depend on isolate_manager