I am having a problem with Flutter/Dart Async code execution, as in how does it work - flutter

In Flutter we use async await and Future, can someone explain that if we don't use another thread (which we can't in dart) and run the job on main UIThread only won't the app become choppy because even if we are waiting for the job to execute it will ultimately execute on UIThread only.
I somewhere read about isolates also. But cannot paint the exact picture. If someone can explain in detail.

I think you missed something when it comes to asynchronous methods in Dart.
Dart is a single-threaded programming language, Java or C# are multi-threaded programming languages, forget async as a new thread, it doesn't happen in Dart.
Dart is a single-threaded programming language. This means is that Dart can only run one instruction at a time, while Java could run multiple instructions concurrently.
As a rule, everything you do in Dart will start in UI-Thread. Whatever method you call in Dart, whether using sync, async, then, they will be running on UI-Thread, since Dart is a single thread.
In single-threaded languages ​​like Javascript and Dart, an async method is NOT executed in parallel but following the regular sequence of events, handled by the Event Loop. There are some problems (I would give some approaches, as we will see below) if you run the following code in a multithreaded language where fetch will take some time to execute:
String user = new Database.fetch(David);
String personalData = new Database.fetch(user);
You will receive David's data in user, and after that, you will receive your data.
This will lock your UI, unlike languages ​​like Java which have Threads, where you can perform this task in the background, on another thread, and the UI-Thread will run smoothly.
If you do this at Dart
String user = new Database.fetch(David);
String personalData = new Database.fetch(user);
user will be null in personalData, because the fetch event is a Future.
How to solve this in Dart?
String user = await Database.fetch(David);
String personalData = await Database.fetch(user);
For those who like a more functional paradigm (I don't like it) you can use then.
Database.fetch(David).then((user){
Database.fetch(user).then((personal){
String personalData = personal;
});
});
However, imagine that you have billions of data in that database, this heavy task will probably cause the animations on your screen to freeze, and you will see a jank in the user's UI, for that purpose isolates were invented.
Dart Isolates give you a way to perform real multi-threading in Dart. They have their own separate heaps(memory), and run the code in the background, just like the Threads of multi-threaded languages. I could explain how isolates work, but it would make this response very long, and the goal is just to differentiate asynchronous from multi-threaded methods.
A simple way to solve the problem above using isolates would be using compute.
Compute was created to facilitate the creation of isolates, you just pass the function and the data that this function will execute, and that's it!
Important to remember that compute is a Future, so you have to use await or then to get its result.
In our example, we could create a new thread and get its result when we finish by just calling compute like this:
String user = await compute(Database.fetch,David);
String personalData = await compute(Database.fetch,user);
Very simple, isn't it?
In summary:
Everything that waits some time to be completed, in Dart is called a "Future".
To wait for the result of a future to be assigned to a variable, use await or then.
The asynchronous methods (await and then) can be used to obtain a result from a Future, and are executed ON THE MAIN THREAD because Dart is single-thread.
If you want to run any function on a new thread, you can create an isolate. Dart offers an easy-to-use isolate wrapper called compute, where you only need to pass one method that will be processed and the data that will be processed, and it will return its result in the future.
NOTE: if you are going to use compute make sure you are using a static or top-level method (see that in the example I used Database.fetch it was no accident if you need to call Database().fetch or need to create an instance of it, means it is not a static method and will not work with isolates).
English is not my first language and I didn't want to write so much because of that, but I hope I helped differentiate between multi-threaded asynchronous programming from single-threaded asynchronous programming.

Related

Flutter: send message to isolate without `await`

The title is one way of doing what I ultimately want; others may be acceptable.
So I have a class that already exists. It controls some other stuff, and has a start and stop method. We are now making the list of "other stuff" depend on some settings saved in shared preferences, but shared preferences requires an asynchronous SharedPreferences.getInstance() call. I don't want to have to propagate asynchronicity all the way up the call chain. How do I avoid that? (Alternative 1: is there a library that you can persist data with, without messing directly with asynchronicity?)
Part of the problem is that the start and stop methods can be called at any point after the constructor (and indeed, when I just did SharedPreferences.getInstance().then(INITIALIZE LATE FINAL FIELDS), without await, start got called before the initialization happened and the code threw an exception.
(Alternative 2: is it acceptable to save the result of a then, and later call then on it? Like, do this._future = SharedPreferences.getInstance().then((prefs) {return LIST_OF_STUFF;}), and in start do this._future.then((listOfStuff) {START LIST OF STUFF; return listOfStuff;}), and similarly in stop? Seems...suspicious, but possibly functional.) I'd be ok with making the start and stop methods asynchronous, I guess, as long as I don't have to actually handle that in the code that calls them, but that doesn't really solve the problem that I need the SharedPreferences.getInstance() to have completed so I can have the list of stuff, first.
My default mechanism for handling concurrency is Communicating Sequential Processes, so I took a leaf from that book and thought, "What if I started an isolate that accepts start/stop messages? It can await the SharedPreferences, then loop over incoming messages and start/stop the Stuff as requested. I wrote an isolate to do that...and apparently you can't pass a ReceivePort in to an isolate (at least not by declaring it in the outer scope and using the reference in the isolate - it gives me an error Invalid argument(s): Illegal argument in isolate message: (object is aReceivePort)). You only get to directly hand the isolate a SendPort, so IT can send THE CALLER messages. The idiom I see in most places is to have the isolate send back a new SendPort, whose ReceivePort the isolate keeps. However, this returns me to my original problem - creating the isolate (and receiving its SendPort) is asynchronous, so it may not be done by the time start or stop get called. Since you pretty clearly CAN send a ReceivePort between...wait, I forgot it's technically not sending a ReceivePort, just another SendPort. Can you really only send SendPorts, specifically? That's...really obnoxious, if so. It seems pointlessly pedantic and obstructive. ...Ok, yeah, ReceivePort is explicitly restricted from being sent. Argh. I even thought I had a workaround to the "can't pass via scope" problem: make a new ReceivePort in the calling code, use its SendPort to send start and stop messages, and jerryrig some kind of handover of the ReceivePort to the isolate once that comes online. But if ReceivePort is entirely forbidden from being sent to the isolate, I guess that's out. So...how do I do what I want to do??? How do I pull a setting from disk, potentially queuing up start and stop messages while I wait for the persistence library to become available, without exposing to the calling code that we're making asynchronous calls under the hood?? Am I gonna have to make some kind of awful kludge of dangerous asynchronously-modified flags? I'll be rather disappointed in Flutter if that's what it comes down to.

Is .indexWhere() async?

As indexWhere() searches through potentially large Lists to find the matching index, is this an async operation?
And if so is there some way to await it? e.g. similar to Future.forEach
Since the method does not return a Future but an int, then the method is synchronous:
https://api.dart.dev/stable/2.7.1/dart-core/List/indexWhere.html
I also want to point out that asynchronous code in Dart are not the same as the code is running in another thread. Since we are talking about finding a index inside a List, then you can be fairly sure that this operation can only be done in a single thread since we need to prevent modifications of the List when we are making the search.

Dart/Flutter: How to best process hardware events in an infinite loop

I have an infinite loop that runs in a an async function in a Flutter application (although the code is pure Dart, no Flutter API is used on it).
Basically, it processes some math stuff, but I want to be able to update the data to be processed with an event coming from hardware (in this specific case from the microphone).
In order to give the fast while(true) the opportunity to get events from the outside, I added a delay with a duration of zero.
doStuff() async {
while (!stopFlag) {
processMathStuff(dataModifiedByHardwareCallback);
// Without this, the loop can't be interrupted.
await Future.delayed(Duration.zero);
}
}
And although that seems to work in most platforms, calling Future.delayed like that makes this loop much slower than I would like. The question is if is it there a better (faster) way of doing this. This even looks kind of a hack to me. I tried calling Future.delayed only every a certain amount of iterations, but doing so even as often as every 10 iterations, the system loses events.
By the way, processMathStuff does not contain loops internally, so it is really O(1).
NOTE: If I move the code to an Isolate, I get a similar problem with the Isolate not listening to the sendPort stuff if the while(true) lacks that kind of "yield".

Should a function returning a boolean be used in an if statement?

If I have a function call that returns true or false as the condition of an if statement, do I have to worry about Swift forking my code for efficiency?
For example,
if (resourceIsAvailable()) { //execute code }
If checking for the resource is computationally expensive, will Xcode wait or attempt to continue on with the code?
Is this worth using a completion handler?
What if the resource check must make a database call?
Good question.
First off... can a function be used? Absolutely.
Second... should it be used?
A lot of that depends on the implementation of the function. If the function is known (to the person who wrote it) to take a long time to complete then I would expect that person to deal with that accordingly.
Thankfully with a lot of iOS things like that are taken out of the hands of the developer (mostly). CoreData and Network requests normally come with a completion handler. So any function that uses them would also need to be async and have a completion handler.
There is no fixed rule for this. My best advice would be...
If you can see the implementation of the function then try to work out what it’s doing.
If you can’t then give it a go. You could even use the time profiler in Xcode profiler to determine how long it is taking to complete.
The worst that could happen is you find it is slow and then change it for something else.

Syncronous Scala Future without separate thread

I'm building a library that, as part of its functionality, makes HTTP requests. To get it to work in the multiple environments it'll be deployed in I'd like it to be able to work with or without Futures.
One option is to have the library parametrise the type of its response so you can create an instance of the library with type Future, or an instance with type Id, depending on whether you are using an asynchronous HTTP implementation. (Id might be an Identity monad - enough to expose a consistent interface to users)
I've started with that approach but it has got complicated. What I'd really like to do instead is use the Future type everywhere, boxing synchronous responses in a Future where necessary. However, I understand that using Futures will always entail some kind of threadpool. This won't fly in e.g. AppEngine (a required environment).
Is there a way to create a Future from a value that will be executed on the current thread and thus not cause problems in environments where it isn't possible to spawn threads?
(p.s. as an additional requirement, I need to be able to cross build the library back to Scala v2.9.1 which might limit the features available in scala.concurrent)
From what I understand you wish to execute something and then wrap the result with Future. In that case, you can always use Promise
val p = Promise[Int]
p success 42
val f = p.future
Hence you now have a future wrapper containing the final value 42
Promise is very well explained here .
Take a look at Scalaz version of Future trait. That's based on top of Trampoline mechanism which will be executing by the current thread unless fork or apply won't be called + that completely removes all ExecutionContext imports =)