Distinguish asynchronous and multithread in Dart

Tram Ho

When starting to learn deeply about Asynchronous and Isolate in Dart, many of you are confused between the two concepts of concurrency and parallelism in Dart. To be able to better understand these two concepts, we must first clarify some of the basic concepts of Dart as follows:

  1. Dart is a Single Threaded language

Dart is a single-threaded language so Dart executes tasks one by one, task after task meaning that as long as an operation is executing, it cannot be interrupted by any other code. In other words, if you run a purely synchronous method , the latter will be the only method executed until it completes. For example:

In the above example, the execution of the myBigLoop() method will never be interrupted until it completes. Therefore, if this method takes some time, the application will be “blocked” during the entire execution of the method.!

2.The Dart execution model

When you run a Flutter App (or any Dart app), a new Thread process (in Dart language = “Isolate”) is created and launched. This thread will be the only thread that you have to care about for the entire application.

  • initialize 2 queues, namely FIFO “MicroTask Queue” and “Event Queue”
  • execute main() method and when this code execution is complete
  • Launch Event Loop

During the entire lifecycle of the thread, an internal and invisible process, called the “Event Loop”, controls how your code will be executed and in what sequence, depending on the contents of the thread. both “MicroTask Queue” and “Event Queue”. Event Loop is an infinite loop, it takes care of 1 main task is to check if the events in the MicroTask Queue are empty, it will push the Event Queue’s events to the main Isolate and then execute it.

As we can see MicroTask Queue is preferred over Event Queue but what are those 2 queues used for? Let’s find out with us:

3.MicroTask Queue

MicroTask Queues are used for very short internal actions that need to be run asynchronously, right after something else completes and before being put back into the Event queue.

4.Event Queue

The event queue is used to reference operations arising from external events such as: I/O, gestures, drawing, timers, streams, etc. Actually, every time an external event is fired , then it will be referenced to the Event queue.

As soon as there are no more micro tasks to run, the Event Loop will look at the first item in the Event Queue and will execute it. It is very interesting that Futures Contracts are also processed through the Event Queue. Please see the picture to understand more

image.png

At this point, I think it’s time to get to the main point. Clearly distinguish the two concepts of concurrency and parallelism. When we use async, we are using the concept of concurrency. When we run a piece of async code, Dart does not automatically create multithread but we can only use 1 thread, the processor will not stop or do anything when encountering an async task. In short, when we perform an async task, the program will “Concurrently” continue to run and it will only “pause” the code that is using the await keyword and push it to the Event Queue and continue executing. Show other code snippets:

image.png

as you can see right after executing the print(“end of loop”); then the Event Loop will immediately push the events in the Event Queue to the main isolation to continue running. I would like to emphasize one thing again:

  • Async does not require automatically turning our program to run multithread but can only need to use 1 thread
  • Async is non-blocking which means that the following code will still be able to work properly but it doesn’t mean it won’t cause your application to “JANK”.

If suppose in the example above, the delayedPrint function runs for too long because it has to handle a heavy task, it will also lead to your app being frozen, so if you have to handle a heavy task, it may cause your app to crash. At this point, we will use the concept of parallelism by using another isolate running in parallel with the main Isolate to handle that heavy task, I will not go deeper into how to use it. Adding an isolate will simply make the two concepts clearer. Each “Isolate” has its own “Event Loop” and Queue (MicroTask and Event). This means that the code runs inside an Isolate, independent of another Isolate. Thanks to that, we can have parallel processing.

Share the news now