小菜前段時間簡單研究了一下 Dart 單線程實作異步的操作,今天繼續學習 Dart 的事件機制的任務排程;
任務排程
Dart 是單線程的,一個 Flutter 程式由一個或多個 isolate 組成,預設的執行方法均是在 main isolate 中;一個 isolate 中包含一個 Event Loop 和一個 Task Queue,而 Task Queue 包含 MicroTask Queue 微事件隊列和 Event Queue 事件隊列兩種;

Dart 的事件機制是根據任務排程優先級來實作的;其中将任務添加到 MicroTask Queue 微事件隊列的方式有 scheduleMicrotask() 和 Future.microtask() 兩種;而将任務添加到 Event Queue 事件隊列一般通過 Future 的相關構造方法實作;
MicroTask Queue 微事件隊列的執行優先級高于 Event Queue 事件隊列,而小菜簡單了解,兩種均類似于 Android 線程中的異步操作,隻是 MicroTask Queue 微事件隊列優先級相對更高;
Dart 的事件執行順序如圖所示;
- 啟動 app 後優先執行 main() 方法中的同步方法;
- 檢視 MicroTask Queue 是否為空,若不為空,優先循環執行 MicroTask Queue 中的 MicroTask,直到隊列為空;
- 若 MicroTask Queue 隊列為空則檢視 Event Queue 事件隊列,若不為空,則循環執行 Event Queue 中的 Event 事件,直到隊列為空;
- 等兩個隊列的任務均執行完成後結束;
Tips: 當任務隊列執行 MicroTask Queue 微事件隊列時,Event Queue 事件隊列被卡住,即應用無法繪制圖形,處理滑鼠點選事件,無法對 I/O 事件做出反應等;
案例嘗試
每個 isolate 有各自的記憶體塊和 Event Loop 是互相隔離的,小菜隻嘗試單個 isolate 中的微事件隊列和事件隊列的執行順序;
- 小菜優先嘗試最基本的 Future 構造函數和 MicroTask 的執行順序;
_taskQueue01() {
Future(() => print('TaskQueue -> Future A'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask B'));
Future(() => print('TaskQueue -> Future C'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
根據上述執行順序,首先執行 main() 中同步的日志輸出 Click start -> end,之後優先執行 MicroTask 微事件 scheduleMicrotask A -> C;再之後執行添加到 EventTask 事件隊列的 Future() 構造函數 Future A -> C;最後執行因延遲 2s 加入事件隊列的 Future B;
- 小菜在【案例一】的基礎上嘗試添加 Future.microtask() 構造函數;
_taskQueue02() {
Future(() => print('TaskQueue -> Future A'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'));
Future(() => print('TaskQueue -> Future C'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
簡單了解 Future.microtask() 函數源碼,其實就是封裝了一個 scheduleMicrotask() 微事件,是以微事件隊列執行順序為 scheduleMicrotask A -> B -> C;
- 小菜在【案例二】的基礎上嘗試添加 Future.sync() 構造函數;
_taskQueue03() {
Future(() => print('TaskQueue -> Future A'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.sync(() => print('TaskQueue -> Future.sync D'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'));
Future(() => print('TaskQueue -> Future C'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
Future.sync() 為同步方法,會立即執行,是以是在 main() 點選同步日志 start 和 end 之間的;
- 小菜嘗試添加 Future.then() 級聯方法;
_taskQueue04() {
Future(() => print('TaskQueue -> Future A'))
.then((_) => print('TaskQueue -> Future A -> then()01'))
.then((_) => print('TaskQueue -> Future A -> then()02'));
Future.sync(() => print('TaskQueue -> Future.sync D'))
.then((_) => print('TaskQueue -> Future.sync D -> then()01'))
.then((_) => print('TaskQueue -> Future.sync D -> then()02'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()01'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()02'));
Future(() => print('TaskQueue -> Future C'))
.then((_) => print('TaskQueue -> Future C -> then()01'))
.then((_) => print('TaskQueue -> Future C -> then()02'));
}
Future.then() 級聯函數是按照順序執行的,執行完第一個 then() 函數後才會執行第二個 then();且小菜了解為 then() 函數會在 Future() 執行之後立即執行,可以看作是存放在微事件隊列中;是以整體的執行順序是 sync()-> sync().then() -> microtask() -> microtask().then() -> Future() -> Future().then() -> delayed() -> delayed().then();
- 小菜在【案例三 + 四】的基礎上添加 Future.whenComplete() 方法;
_taskQueue05() {
Future(() => print('TaskQueue -> Future A'))
.then((_) => print('TaskQueue -> Future A -> then()01'))
.then((_) => print('TaskQueue -> Future A -> then()02'))
.whenComplete(() => print('TaskQueue -> Future A -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.sync(() => print('TaskQueue -> Future.sync D'))
.then((_) => print('TaskQueue -> Future.sync D -> then()01'))
.then((_) => print('TaskQueue -> Future.sync D -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.sync D -> whenComplete()'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.delayed B -> whenComplete()'));
Future.microtask(() => print('TaskQueue -> Future.microtask B'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()01'))
.then((_) => print('TaskQueue -> Future.microtask B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.microtask B -> whenComplete()'));
Future(() => print('TaskQueue -> Future C'))
.then((_) => print('TaskQueue -> Future C -> then()01'))
.then((_) => print('TaskQueue -> Future C -> then()02'))
.whenComplete(() => print('TaskQueue -> Future C -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
}
Future.whenComplete() 是在 Future() 及 then() 函數執行完成之後立即執行;小菜也簡單看作是存放在微事件隊列中;依照小菜的測試案例來看,sync 執行同步任務完成後,會循環周遊微事件隊列,其中 scheduleMicrotask A 是在 sync() 函數之前加入 MicroTask Queue 中,是以是先執行 scheduleMicrotask A 再執行 sync() 之後的 then() 和 whenComplete() 方法;
- 小菜嘗試在 then() 函數中執行新的事件隊列;
_taskQueue06() {
Future(() => print('TaskQueue -> Future A'))
.then((_) {
print('TaskQueue -> Future A -> then()01');
return Future.delayed(Duration(seconds: 1), () => print('TaskQueue -> Future.delayed D'));
})
.then((_) => print('TaskQueue -> Future A -> then()02'))
.whenComplete(() => print('TaskQueue -> Future A -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.delayed B -> whenComplete()'));
Future(() => print('TaskQueue -> Future C'))
.then((_) {
print('TaskQueue -> Future C -> then()01');
return scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
})
.then((_) => print('TaskQueue -> Future C -> then()02'))
.whenComplete(() => print('TaskQueue -> Future C -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask B'));
}
a. 首先執行 MicroTask Queue 微事件隊列中的 scheduleMicrotask A -> B;
b. 之後以 FIFO 先進先出的順序執行 EventTask Queue 事件隊列中 Task;先執行 Future A,之後是 A 中 then()01,此時小菜設定了一個 1s 的延遲 Future.delayed D,因為 then() 的級聯函數是需要等前面的 then() 函數執行完成後才能執行;是以優先執行 EventTask Queue 中的 Future C;
c. 執行完 Future C 之後執行 then()01,此時小菜設定了一個 MicroTask,依照小菜的了解,then() / whenComplete() 均存放在 MicroTask Queue 中,是以新設定的 MicroTask 會放置在 MicroTask Queue 末尾,等 then()02 和 whenComplete() 再執行;
d. 此時 EventTask Queue 事件隊列中已執行完畢,在 1s 後添加了新的 Future.delayed D 并執行;
e. 待 Future.delayed D 執行結束或,Future A 的第二個級聯 then() 函數和 whenComplete() 開始執行;
f. 最後執行延遲最久的 2s 的 Future.delayed B 及其 then() / whenComplete() 函數;
- 小菜在測試過程中,與【案例六】代碼幾乎一緻,隻是在 Future.then() 中調用 Future() 時少了 return;
_taskQueue07() {
Future(() => print('TaskQueue -> Future A'))
.then((_) {
print('TaskQueue -> Future A -> then()01');
Future.delayed(Duration(seconds: 1), () => print('TaskQueue -> Future.delayed D'));
})
.then((_) => print('TaskQueue -> Future A -> then()02'))
.whenComplete(() => print('TaskQueue -> Future A -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask A'));
Future.delayed(Duration(seconds: 2), () => print('TaskQueue -> Future.delayed B'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()01'))
.then((_) => print('TaskQueue -> Future.delayed B -> then()02'))
.whenComplete(() => print('TaskQueue -> Future.delayed B -> whenComplete()'));
Future(() => print('TaskQueue -> Future C'))
.then((_) {
print('TaskQueue -> Future C -> then()01');
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask C'));
})
.then((_) => print('TaskQueue -> Future C -> then()02'))
.whenComplete(() => print('TaskQueue -> Future C -> whenComplete()'));
scheduleMicrotask(() => print('TaskQueue -> scheduleMicrotask B'));
}
小菜測試發現,雖然隻是少了兩個 return 但是執行順序卻變化很大;
a. 首先執行微事件隊列中的 scheduleMicrotask A -> B;
b. 之後執行 EventTask Queue 中的 Future A,執行第一個 print(then()01) 之後,小菜設定了 Future.delayed D,因為無需傳回,是以将 Future.delayed D 事件隊列末位後第一個 then() 函數已完成執行,此時可以執行第二個 then()02 和 whenComplete() 函數;
c. 繼續執行 EventTask Queue 中的 Future C,執行第一個 print(then()01) 之後,小菜設定了 scheduleMicrotask() C,無需 return,将 scheduleMicrotask() C 添加到微事件隊列末位,此時第一個 then() 函數以完成執行,執行微事件隊列中的第二個 then()02 和 whenComplete() 函數,再之後執行微事件隊列中最後的 scheduleMicrotask() C;
d. 之後執行事件隊列末位的 1s 的 Future.delayed D,此時事件隊列已為空;
e. 最後執行 2s 添加到事件隊列中的 Future.delayed B 及其 then() / whenComplete() 函數;
彙總小結
- Dart 的任務排程模型是單線程輪詢,不是基于時間排程的;我們可以知道各個事件的排程順序,但無法明确得知各個事件排程的時間;例如:延遲 2s 并非一定是 2s 後執行,需要等之前的任務排程結束後才會執行;
- 注意級聯函數中的新的任務排程是否需要傳回結果後再繼續;
- 實際中盡量在 EventTask 中執行耗時操作,減少在 MicroTask 中執行;若任務耗時時間較長可以嘗試用 isolate 開啟新的異步線程執行;
小菜對任務排程的了解還不夠深入,如有錯誤請多多指導!
來源: 阿策小和尚