Skip to content

Isolates are way too complex for a standard user #50456

Open
@bernaferrari

Description

@bernaferrari

I've been trying to use Dart Isolates for the past few days and it's been a total nightmare. Most of the documentation and tutorials ask to use compute from Flutter. But compute returns the result, not the context, so it is not cancellable. Then, the function that you pass must be in the root level, not in the function level, which makes the error message unclear:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: (object extends NativeWrapper - Library:'dart:ui' Class: Path)

I heard there is also a limitation where you can't return every type, like only primitive types work. And that it copies the values when returning. So, making a simple Isolate from hand is really really hard, there are many edge scenarios, and sometimes it is slower. It is done in a way no one should use, except most people could benefit from parallelism.

I understand how it is thread-safe, memory-safe, and these things are great. But it is implemented in such a non-intuitive way that no uses it in normal situations.

I wish Dart could learn some lessons from coroutines or goroutines and make "async"/multi-thread/parallelism easy to use for anyone. A few months ago, there was describeEnum in Flutter, now Dart takes care of it. Now, I wish there were some care for compute, so hopefully one day it can be deprecated in Flutter with a better solution.

How it is right now (got this code somewhere on stack overflow):

Future<Person> fetchUser() async {
      ReceivePort port = ReceivePort();
      String userData = await Api.getUser();
      final isolate = await Isolate.spawn<List<dynamic>>(
          deserializePerson, [port.sendPort, userData]);
      final person = await port.first;
      isolate.kill(priority: Isolate.immediate);
      return person;
}

void deserializePerson(List<dynamic> values) {
    SendPort sendPort = values[0];
    String data = values[1];
    Map<String, dynamic> dataMap = jsonDecode(data);
    sendPort.send(Person(dataMap["name"]));
}

How it could be:

Future<Person> fetchUser() async {
    final person = await launch(Thread.io, () => deserializePerson(userData));
}

The scoping mechanism with launch and useContext in Kotlin is perfect to me. It is not memory safe, but the scope helps it be easy to use and intuitive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.library-isolate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions