Skip to content

Flutter's compute function instead of Isolate.run()? #7081

@randomizedcoder

Description

@randomizedcoder

Page URL

https://dart.dev/language/isolates#implementing-a-simple-worker-isolate

Page source

No response

Describe the problem

G'day,

This is a follow on from #7044 and flutter/flutter#179083

The dart.dev page says:

https://dart.dev/language/isolates#implementing-a-simple-worker-isolate

Image
Flutter note
If you're using Flutter, you can use [Flutter's compute function](https://api.flutter.dev/flutter/foundation/compute.html) instead of Isolate.run().

1)

on Flutter Web we can call the compute() function from the Flutter API
(also, all Isolate.run() calls from Dart API simply fail in Flutter Web).
Look also here, it is mentioned:
https://api.flutter.dev/flutter/foundation/compute.html

Image

As evidence that in Flutter Web, compute() does nothing except wrap code in a Future
(which means that there are no real Isolates behind the "compute()" call in the Web),
Look at these files from the latest stable Flutter 3.38.5 on Today:
https://github.com/flutter/flutter/blob/3.38.5/packages/flutter/lib/src/foundation/isolates.dart
https://github.com/flutter/flutter/blob/3.38.5/packages/flutter/lib/src/foundation/_isolates_web.dart

Image

2)

A mentioning "Worker" like "Background Worker" or "Simpler Worker Isolate" here
https://dart.dev/language/isolates#implementing-a-simple-worker-isolate
Has nothing related to Web Workers or Workers in JavaScript, which is the way
to achieve real multi-threading in Flutter Web Applications compiled to JavaScript.

Image

3)

I dove deeper into the packages I mentioned earlier:

a) https://pub.dev/packages/worker_manager
Even if there is a declared Web as a supported platform,
Actually, it doesn't work as expected in Flutter Web,
So any heavy computation will simply freeze the UI and the app will become completely unresponsive.

It works as a common Flutter call compute() that simply wraps a call in a Future without any real Isolates.
Here is a ticket, even though it is closed, it was not resolved.
dsrenesanse/worker_manager#100
The author said something about WASM, but I am not sure what and how that is possible.
How a simple Future call will become an Isolate / Worker even if the Flutter Web app was compiled to WASM instead of JS???

But the interesting detail, that this package is mentioned in public Flutter docs here:
https://docs.flutter.dev/perf/isolates#stateful-longer-lived-isolates

Image

b) https://pub.dev/packages/isolate_manager
This package works in Flutter Web.
It provides support for Multi-Threading in Flutter Web using Worker() of JavaScript.
https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker

It has a helper package called
https://pub.dev/packages/isolate_manager_generator
It is used like this

dart run isolate_manager:generate

The call will generate .js files that will be placed by default into the "web" (root of the Flutter Web app) folder, with the code to be executed in Workers.
Later, when the Flutter web is run in the Browser, it will create Worker() JS objects and pass a URL to these .js files in the constructor.

The isolate_manager package is pretty powerful; it also supports a pool (more than 1 Isolate/Worker) to run at the same time, priority strategies, etc.
And most importantly, it really works for the Web.

I tested the package well, so we can run any Dart code in it without freezing the UI (Main Isolate or Main Thread).
For instance, I tested calls with recursion that really take like Fibonacci with recursion, and some heavy algorithms in for loops, and all of them don't freeze UI.

So, if you set up all things right with this package isolate_manager, in Flutter Web code will be executed in a Worker or Workers instead of the main event queue.

Of course, I reproduced this way manually, without isolate_manager package, only Flutter API, and it works, of course, the same way, without freezing the UI or main thread.

To remember, in Flutter Web, using the compute() call and the "worker_manager" package will lead to freezing the UI for any synchronous operation that takes time, like calculating Fibonacci of 45 with recursion, but with "isolate_manager" or pure explicit Worker() usage, that fibonacci call won't freeze UI / Main Thread of the Flutter Web app.

But this isolate_manager package is good, because it already has a built-in generator that generates appropriate .js code and creates implicitly Worker() objects at runtime. And, of course, it supports native platforms, pool, caching, and queue strategies.

Conclusion

  • Using the power of Worker(), we can really create Multi-Threaded Flutter Web apps.
    That is exactly what the isolate_manager package does.
  • I tested heavy computations with recursion and without, keeping a Progress Bar showing, and it was not frozen. Any heavy synchronous operation, no matter how many seconds was lasted (10, 20, 40, etc.), never froze the Main Isolate and the Progress Bar continued its animation, and the whole app was responsive.
    Other built-in compute() and or usage of worker_manager froze the Main Isolate, and the Progress Bar was simply frozen until any heavy computation ended, as the whole app was frozen (no clicks no scrolls no inputs). So the app in my tests did not respond for 8 or 20, or even more seconds.
  • I also tested and confirmed that working with the "worker_manager" package,
    except for simple functions like computing fibonacci,
    we can also call any Dart API: I tried packages like http, bloc, even grpc calls, and all work!
  • But each time when we make some changes in the code related to Isolates when working with the isolate_manager package,
    we have to call dart run isolate_manager:generate to re-generate the necessary .js files
  • And as I said, we can create more than one Isolate/Worker at the same time, which is also great, using the isolate_manager package.
    And we can work not with simple functions that only return some result after executing, but we can write code that will have bi-directional communication, e.g. the code that does some job in a separate Isolate / Worker can post its progress to the main isolate, but still execute until it finishes with a result. Or, Main Isolate can send to a separate Isolate / Worker (Isolates/ Workers) some event, like stop computing, pause/resume the operation, or finish the current result, or simply interrupt the task, etc.
  • With isolate_manager I tested to execute code in a separate isolate when IsolateManager() object was instantiated with concurrent param e.g. 3, so by default, when it gets the first 3 tasks, until they are executed and finished, or one of them, other ongoing tasks will be simply in the queue to be executed. That concurrent parameter tells IsolateManager how many Isolates/Workers will be spawned to execute the code at the same time. But of course, we can specify the concurrent parameter to 4 or 8 if we actually need more than that. But this package supports various strategies for the queue.

Kind regards,
Dave

@antfitch @sfshaza2 @tirth-patel-nc

Expected fix

Concurrency is a very important feature, and it would be really great if the flutter team could accurately document Flutter web concurrency best practices.

e.g. This video makes Isolates sounds great, but I don't think it mentions web isn't supported.
https://www.youtube.com/watch?v=yUMjt0AxVHU

Additional context

No response

I would like to fix this problem.

  • I will try and fix this problem on dart.dev.

Metadata

Metadata

Assignees

No one assigned

    Labels

    e2-daysCan complete in < 5 days of normal, not dedicated, workfrom.page-issueReported in a reader-filed concernp.isolatesp2-mediumNecessary but not urgent concern. Resolve when possible.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions