iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
1

我們在昨天的文章裡提到,每一個Isolate只會有單一執行緒,而我們在這個執行緒上所有的異步處理,都是靠Event Loop機制來完成的。今天我們就繼續來看看這個Event Loop到底是怎麼運作的。

程式啟動

當我們啟動一個Dart應用程式時,Dart會進行幾個步驟:

  1. 建立main Isolate
  2. 在main Isolate中建立MicrotaskEvent兩個FIFO Queue
  3. 執行main()函數
  4. main()執行結束後,啟動Event Loop
  5. Event Loop從Microtask Queue中依序取出task並執行,直到queue清空
  6. Event Loop從Event Queue中取出一個event並執行
  7. 重複5~6,直到Event Queue清空
  8. 程式終止

我們可以看到Dart會先處理完所有的Microtask,再開始處理Event。Microtask到底是什麼?為什麼這麼重要?

Microtask

Microtask是我們平常很少會接觸到的東西。簡單來說,它扮演的角色就相當於是一個Event的onComplete,主要應用是在下一個Event開始之前,進行一些這一個Event的清理動作,例如通知更新,關閉/釋放資源等等。如果有須要,我們可以呼叫scheduleMicrotask來將task排入Microtask Queue。不過實際上在編寫Flutter應用程式時,我們很少會須要以Event為單位來進行這些清理,通常都是隨著Widget Lifecycle進行。即使是在整個Flutter專案中,使用到scheduleMicrotask的地方也不多:
https://ithelp.ithome.com.tw/upload/images/20200920/201290530P8XQ3dmZK.png
來看看其中幾個例子:
https://ithelp.ithome.com.tw/upload/images/20200920/20129053i9GktKl7T3.png
https://ithelp.ithome.com.tw/upload/images/20200920/20129053Pm6MIKo7ZB.png

Event

Event對我們來說就相對熟悉了,像是各種I/O、手勢、計時器、Vsync、來自其它Isolate的訊息,所有Future和async function的呼叫,都會被加入Event Queue裡。值得注意的是,這是一個FIFO Queue,代表Event之間沒有任何權重關係,我們也沒有辦法直接把Event插隊到Queue中間,更不可能打斷現在正在執行的Event。也就是說,當我們執行

Future.delayed(Duration(seconds: 5), (){ print("Hello"); });

它並不會剛好在5秒後執行,只是會在5秒後被加入Event Queue而已。那時候可能Event Loop正在執行一個長時間的Event,或它前面已經排了很多其它Event。

Future

首先我們來看看怎麼建立Future。雖然Dart提供了幾種建立Future的方式:

  Future(() { });
  Future.delayed();
  Future.value();
  Future.sync(() => null)
  Future.microtask(() => null);

但其中只有Future()Future.delayed()會被加入Event Queue,其它三個都是直接進入Microtask Queue。有趣的是,Future()和Future.delayed()其實都是靠Timer來實作的,只是包上了一些錯誤處理的try/catch而已。

  factory Future(FutureOr<T> computation()) {
    _Future<T> result = new _Future<T>();
    Timer.run(() {
      try {
        result._complete(computation());
      } catch (e, s) {
        _completeWithErrorCallback(result, e, s);
      }
    });
    return result;
  }

建立Future,並將它加入Event Loop之後,我們該怎麼在它執行完畢時,得到結果並進行後續處理呢?當然現在我們可以使用await語法,不過再這之前,我們主要是透過Future的三個callback:

  Future(() => "Hello")
      .then((value) => value + "World")
      .then((value) => print(value))
      .catchError((error) { print(error); })
      .whenComplete(() => print("Completed!"));

這些callback會在Future完成後立刻被呼叫,而不會加入Event Queue中。也就是說不論我們串了多少then,它們全部會在同一個Event中依序被執行。但如果在我們串接then之前,Future就已經完成了,這時候then會被加入MicroTask Queue,以確保它在下個Event開始之前完成。

async/await

接著我們來看看async函數實際上是怎麼被加入Event Loop的:

void main() {
  Future(() {
    print("long event start");
    DateTime end = DateTime.now().add(Duration(seconds: 5));
    while (DateTime.now().isBefore(end));
    print("long event end");
  });
  asyncFunction();
}

Future<void> asyncFunction() async {
  print("async function start");
  await Future(() {
    print("async function await start");
    DateTime end = DateTime.now().add(Duration(seconds: 5));
    while (DateTime.now().isBefore(end));
    print("async function await end");
  });
  print("async function end");
}

如果我們執行這段程式碼,得到的結果會是:

I/flutter (10076): async function start
I/flutter (10076): long event start
I/flutter (10076): long event end
I/flutter (10076): async function await start
I/flutter (10076): async function await end
I/flutter (10076): async function end

程式執行順序如下:

  1. main開始執行
  2. 建立longEevent並加入Event Loop
  3. 進入asyncFunction執行print("async function start")
  4. 將asyncFunction中,包含await後的所有程式碼,加入Event Loop
  5. main結束執行
  6. Event Loop執行longEvent
  7. Event Loop執行asyncFunction剩下的程式碼

第四點是瞭解async/await機制最重要的一點。當我們呼叫一個async函數時,並不是把它整個加入Event Loop然後繼續執行,而是先執行到第一個await之前,剩下的才加入Event Loop。如果同一段程式碼裡面有好幾個await呢?

Future<void> asyncFunction() async {
  print("start");
  await Future(() {});
  print("future 1 finished");
  await Future(() {});
  print("future 2 finished");
  await Future(() {});
  print("future 3 finished");
}

當然,每次程式執行到await就會中斷,把剩下的所有程式碼丟進Event Loop,然後繼續執行目前的Event。

結語

我們一開始說明了Dart程式的啟動流程,瞭解了Event Loop中Microtask和Event Queue各自的任務和運作機制,最後也看到了我們平常熟悉的Future/async/await,它們背後的Event Queue運作細節。到這裡希望你有更瞭解整個Event Loop的運作機制了,下次當你被程式碼中層層疊疊的Future/async/await搞得暈頭轉向,不知道它們到底會如何執行時,只要想想它們是怎麼被加入Event Loop的,相信一切就會豁然開朗了。


上一篇
days[18] = "Isolate是怎麼運作的?"
下一篇
days[20] = "Render Pipeline是怎麼運作的?"
系列文
Why Flutter why? 從表層到底層,從如何到為何。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言