Flutter 事件機制 - Future 和 MicroTask 全解析

大師兄QZW發表於2019-04-10

寫在前面

瞭解過Flutter的同學都知道,不同於 Android 原生開發,dart 是單執行緒實體的語言,所以我們一般的非同步操作,實際上還是通過單執行緒通過排程任務優先順序來實現的,就是我們經常用到的 Future,但是Flutter中的事件機制究竟是怎樣的?多個Future 和 Microtask 程式的執行順序是怎樣的? 本文將藉助兩個比較複雜的例子來詳細介紹 Flutter 的事件機制,希望能對大家有所幫助。

Main程式碼塊,EventQueue,MicrotaskQueue 的執行優先順序

首先我得提一嘴 isolate(隔離),isolate是有自己的記憶體和單執行緒控制的執行實體,isolate類似於執行緒。 執行中的 Flutter 程式由一個或多個 isolate 組成。我們的程式碼預設都在 Main isolate中執行。
為了保持高的響應性,特別耗時的任務一般不要放在Main isolate 中。但 isolate 不是本文的重點,在此就不過多贅述。

Dart 中事件機制的實現 :Main isolate 中有一個Looper,但存在兩個Queue:Event Queue 和 Microtask Queue 。
因為 isolate 是單執行緒實體,所以 isolate中的程式碼是按順序執行的。
所以 dart 中的程式碼執行優先順序可以分為三個級別:

  1. 在 Main 中寫程式碼將最先執行;
  2. 執行完 Main 中的程式碼,然後會檢查並執行 Microtask Queue 中的任務, 通常使用 scheduleMicrotask 將事件新增到 MicroTask Queue 中;
  3. 最後執行 EventQueue 佇列中的程式碼,通常使用 Future 向 EventQueue加入時間,也可以使用 async 和 await 向 EventQueue 加入事件。

總結:Dart 中事件的執行順序:Main > MicroTask > EventQueue。
如圖:

Flutter 事件機制 - Future 和 MicroTask 全解析
驗證:

void testSX(){
  new Future(() => print('s_1'));
  scheduleMicrotask(() => print('s_2'));
  print('s_3');
}
複製程式碼

輸出結果:

I/flutter (32415): s_3
I/flutter (32415): s_2
I/flutter (32415): s_1
複製程式碼

Future簡介

前面講到,用 async 和 await 組合,即可向 event queue 中插入 event 實現非同步操作,那為什麼還會有Future呢?
其實,Future 最主要的功能就是提供了鏈式呼叫。

new Future (() => print('拆分任務_1'))
    .then((i) => print('拆分任務_2'))
    .then((i) => print('拆分任務_3'))
    .whenComplete(()=>print('任務完成'));
複製程式碼

Future中的 then 並沒有建立新的Event丟到Event Queue中,而只是一個普通的Function,在一個 Future 所有的 Function 執行完後,下一個 Future 才會開始執行。

多個 Future 的執行順序

  1. 規則一:Future 的執行順序為Future的在 EventQueue 的排列順序。類似於 JAVA 中的佇列,先來先執行。
  2. 規則二:當任務需要延遲執行時,可以使用 new Future.delay() 來將任務延遲執行。
  3. 規則三: Future 如果執行完才新增 than ,該任務會被放入 microTask,當前 Future 執行完會執行 microTask,microTask 為空後才會執行下一個Future。
  4. 規則四:Future 是鏈式呼叫,意味著Future 的 then 未執行完,下一個then 不會執行。

理論結束,然後來看一段程式碼吧:

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');
}
複製程式碼

各位同學可以試著寫一下結果,然後對比下輸出結果。

輸出結果:

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
複製程式碼

是不是跟自己的結果大相徑庭,別急,看我來慢慢分析: 分析:

  1. 首先執行Main 的程式碼,所以首先輸出: 8;
  2. 然後參考上面的規則1,Future 1 到 5 是按初始化順序放入 EventQueue中,所以依次執行Future 1到5 , 所以輸出結果:8,1,7。
  3. 參考規則2,f3 延時執行,一定是在最後一個:8,1,7,…,2。
  4. 在 f4 中,首先輸出 f4 :8,1,7,4,…,2。
  5. 在 f4 的 then 的方法塊中,新建了Future, 所以新建的 Future 將在 EventQueue尾部,最後被執行:8,1,7,4,…,5,2。
  6. 在 f4 的 then 的方法塊中,給 f2 新增了 then ,但此時 f2 已經執行完了,參考規則三,所以 then 中的程式碼會被放到 microTask 中,在當前 Future 執行完後執行。 因為此時Future f4已經執行完了,所以會處理microTask(microTask優先順序高)。結果:8,1,7,4,6,..,5,2。
  7. 此時我們的 EventQueue 中還有 f5,和在 f4 中新增的新的Future。 所以我們的最終結果就是:8,1,7,4,6,3,5,2。

是不是有點理解不了,沒事,牢記四個規則,自己再算一遍,相信你就瞭然於胸了。重要要在腦海裡有一個 EventQueue 的佇列模型,牢記先進先出。

然後來試一試下一題:

多Future 和 多micTask 的執行順序

void testScheduleMicrotatsk() {
  scheduleMicrotask(() => print('Mission_1'));

//註釋1
  new Future.delayed(new Duration(seconds: 1), () => print('Mission_2'));

//註釋2
  new Future(() => print('Mission_3')).then((_) {
    print('Mission_4');
    scheduleMicrotask(() => print('Mission_5'));
  }).then((_) => print('Mission_6'));

//註釋3
  new Future(() => print('Mission_7'))
      .then((_) => new Future(() => print('Mission_8')))
      .then((_) => print('Mission_9'));

//註釋4
  new Future(() => print('Mission_10'));

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

  print('Mission_12');
}

複製程式碼

大家可以先自己試一下,再對照結果~

輸出結果:

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
複製程式碼

是不是還是沒答全對?沒關係,很正常,看我慢慢道來:
分析:

  1. 根據 Main > MicroTask > EventQueue。我們首先會得到輸出結果:12,1,11。
  2. 註釋1 的 Future 是延時執行,所以:12,1,11,…,2。
  3. 註釋2 中建立了 Microtask,Microtask會在該Future執行完後執行,所以:12,1,11,4,6,5,…,2。
  4. 重點來了。我們在註釋3 的Future 的 then 中新建了Future(輸出Mission_8),新建的 Future 將被加到 EventQueue尾部,並且,註釋3的Future後續的then將不再執行,因為這個鏈被阻塞了!
    注意對比上一題中的 f4, 上一題中的 f4 是一個 than 方法包裹了程式碼塊。
    此時的結果:12,1,11,4,6,5,7,…,2。
  5. 執行完註釋4 的 Future,然後會執行我們在註釋3 Future 新加入的 Future,之後註釋3 的Future不再阻塞,會繼續執行,結果: 12,1,11,4,6,5,7,10,8,9,2。

看到這裡,相信各位同學已經對 Dart 事件機制有一個大概的瞭解,希望能對 各位在學Flutter 的同學有所幫助,蟹蟹~

END

我是雷加,如果您喜歡我的文章,請留下你的贊;如有疑問和建議,請在評論區留言
我的 Github, 歡迎關注~

相關文章