Writing in the front

Those of you who have read about Flutter know that dart is a single-threaded entity language, unlike Android’s native development. Therefore, our normal asynchronous operation is actually achieved through single-threaded scheduling task priorities. This is the Future we often use. But what exactly is the mechanism of events in Flutter? What is the order in which multiple Futures and Microtask programs are executed? This paper will introduce the event mechanism of Flutter in detail with the help of two complex examples.

The execution priority of the Main code block, EventQueue, MicrotaskQueue

The first thing I need to mention is that the ISOLATE is an operating entity with its own memory and single thread control. The ISOLATE is similar to threads. The running Flutter program consists of one or more isolations. Our code is executed on Main ISOLATE by default. In order to maintain high responsiveness, particularly time-consuming tasks are generally kept out of the Main ISOLATE. However, ISOLATE is not the focus of this article, so I won’t talk about it here.

Dart Event mechanism implementation: Main ISOLATE has a Looper, but there are two queues :Event Queue and Microtask Queue. Because the ISOLATE is a single-threaded entity, the code in the ISOLATE executes sequentially. So there are three levels of code execution priority in DART:

  1. Writing code in Main will be executed first;
  2. After executing the code in Main, tasks in the Microtask Queue are checked and executed. ScheduleMicrotask is usually used to add events to the Microtask Queue.
  3. Finally, the code in the EventQueue is executed, usually adding time to the EventQueue using a Future, or adding events to the EventQueue using async and await.

Summary: Dart events are executed in Main > MicroTask > EventQueue. As shown in figure:

void testSX(){
  new Future(() => print('s_1'));
  scheduleMicrotask(() => print('s_2'));
  print('s_3');
}
Copy the code

Output result:

I/flutter (32415): s_3
I/flutter (32415): s_2
I/flutter (32415): s_1
Copy the code

Introduction of the Future

We can use the async and await combination to insert events into the event queue for asynchronous operations. Why should there be a Future? In fact, the main function of Future is to provide chain calls.

new Future (() => print('Split task _1'))
    .then((i) => print('Split task _2'))
    .then((i) => print('Split task _3'))
    .whenComplete(()=>print('Mission completed'));
Copy the code

The then in the Future does not create a new Event and throw it into the Event Queue. Instead, it is a normal Function. After all functions in a Future have been executed, the next Future will start executing.

Execution order of multiple Futures

  1. Rule 1: Futures are executed in the order in which they are placed in the EventQueue. Similar to queues in JAVA, first come, first executed.
  2. Rule 2: When a task needs to be delayed, use new future.delay () to delay the execution of the task.
  3. Rule 3: If you add than to a Future after it has been executed, the task will be put into a microTask. The microTask will be executed after the current Future has been executed, and the next Future will be executed after the microTask is empty.
  4. Rule 4: Future is a chained call, meaning that the Future’s THEN is not executed, and the next THEN will not be executed.

With the theory out of the way, let’s look at some code:

void testFuture() {
  Future f1 = new Future(() => print('f1'));
  Future f2 = new Future(() =>  null);
  Future f3 = new Future.delayed(Duration(seconds: 1) ,() => print('f2'));
  Future f4 = new Future(() => null);
  Future f5 = new Future(() => null);

  f5.then((_) => print('f3'));
  f4.then((_) {
    print('f4');
    new Future(() => print('f5'));
    f2.then((_) {
      print('f6');
    });
  });
  f2.then((m) {
    print('f7');
  });
  print('f8');
}
Copy the code

Students can try to write down the results, and then compare the output.

Output result:

com.example.flutter_dart_app I/flutter: f8
com.example.flutter_dart_app I/flutter: f1
com.example.flutter_dart_app I/flutter: f7
com.example.flutter_dart_app I/flutter: f4
com.example.flutter_dart_app I/flutter: f6
com.example.flutter_dart_app I/flutter: f3
com.example.flutter_dart_app I/flutter: f5
com.example.flutter_dart_app I/flutter: f2
Copy the code

Is it different from their own results, don’t worry, see me slowly analysis:

  1. The Main code is executed first, so the output is: 8;
  2. Then referring to rule 1 above, Future 1 through 5 are placed in the EventQueue in the order in which they are initialized, so Future 1 through 5 is executed in sequence, so the output is 8,1,7.
  3. Refer to rule 2, f3 delay execution, must be at the last: 8,1,7… . 2.
  4. In f4, first output f4:8,1,7,4… . 2.
  5. In f4’s then method block, a new Future is created, so the new Future will be at the end of the EventQueue and executed last: 8,1,7,4… Five, two.
  6. In the method block of F4’s THEN, then is added to F2, but f2 has already executed, refer to rule 3, so the code in then is put into microTask and executed after the current Future is executed. Since the Future F4 has finished executing at this point, microtasks will be processed (microtasks have a higher priority). Results: 8,1,7,4,6,.. Five, two.
  7. At this point we also have F5 in our EventQueue and the new Future added to f4. So our final result is: 8,1,7,4,6,3,5,2.

Is not a little understand not, have no matter, keep in mind the four rules, oneself calculate again, believe you know clearly in the chest. It’s important to have an EventQueue model in mind, and remember first in, first out.

Then let’s try the next one:

Multi-future and multi-micTask execution order

void testScheduleMicrotatsk() {
  scheduleMicrotask(() => print('Mission_1')); New future.delayed (new Duration(seconds: 1), () =>print('Mission_2')); // annotation 2 New Future(() =>print('Mission_3')).then((_) {
    print('Mission_4');
    scheduleMicrotask(() => print('Mission_5'));
  }).then((_) => print('Mission_6')); // annotation 3 New Future(() =>print('Mission_7'))
      .then((_) => new Future(() => print('Mission_8')))
      .then((_) => print('Mission_9')); // annotation 4 New Future(() =>print('Mission_10'));

  scheduleMicrotask(() => print('Mission_11'));

  print('Mission_12');
}

Copy the code

You can first try their own, and then compare the results ~

Output result:

I/flutter (19025): Mission_12
I/flutter (19025): Mission_1
I/flutter (19025): Mission_11
I/flutter (19025): Mission_3
I/flutter (19025): Mission_4
I/flutter (19025): Mission_6
I/flutter (19025): Mission_5
I/flutter (19025): Mission_7
I/flutter (19025): Mission_10
I/flutter (19025): Mission_8
I/flutter (19025): Mission_9
Syncing files to device MIX 3...
I/flutter (19025): Mission_2
Copy the code

Are you still not getting all the answers right? It’s ok. It’s normal. Watch me slowly.

  1. See Main > MicroTask > EventQueue. We first get the output: 12,1,11.
  2. Note 1’s Future is delayed, so: 12,1,11… . 2.
  3. Note 2 creates a Microtask that will execute after the Future executes, so: 12,1,11,4,6,5… . 2.
  4. Here’s the point. We create a new Future in the then of the Future in comment 3 (output Mission_8). The new Future will be added to the end of the EventQueue, and the subsequent THEN of the Future in comment 3 will not be executed because the chain is blocked! Note that in contrast to f4 in the previous problem, F4 is a than method wrapped around the code block. Results: 12,1,11,4,6,5,7… . 2.
  5. After executing the Future in comment 4, the Future we added in comment 3 will be executed. After that, the Future in comment 3 will no longer block and continue executing, resulting in 12,1,11,4,6,5,7,10,8,9,2.

Read this, I believe you have a general understanding of the Dart event mechanism, I hope it can be helpful to students learning Flutter

END

I’m Rhaegar, if you like my article, please leave a like; If you have any questions or suggestions, please leave them on Github in the comments section