問題
1:microtask queue
的優先級高於 event queue
,所以如果 microtask queue
有太多的微任務, 那么就可能會霸占住當前的event loop
。
2:then優先級高於Flutter
3: isolate高於微任務
目錄
預備
我們所熟悉的前端開發框架大都是事件驅動的。事件驅動意味着你的程序中必然存在事件循環和事件隊列。事件循環會不停的從事件隊列中獲取和處理各種事件。也就是說你的程序必然是支持異步的。
在Android中這樣的結構是Looper/Handler;
在iOS中是RunLoop;
在JavaScript中是Event Loop。
同樣的Flutter/Dart也是事件驅動的,也有自己的Event Loop。而且這個Event Loop和JavaScript的很像,很像。(畢竟Dart是想替換JS來着)。下面我們就來了解一下Dart中的Event Loop。
/* * 1,需要注意返回值,=>是return的簡寫,所以1、2、3的then是compute的后續,所以是在多線程中執行,無序的。 * 2,Future和自己的then是一體的,必定會同步執行,而scheduleMicrotask則是后續添加的所以最后執行 * * */ void combinText() { Future(() => compute(comFunc, '1')).then((val) => print(val)); //1 Future(() => compute(comFunc, '2')).then((val) => print(val)); Future(() => compute(comFunc, '3')).then((val) => print(val)); Future(() { compute(comFunc, '4'); return '4處理'; }).then((val) => print(val)); Future(() { compute(comFunc, '5'); return '5處理'; }).then((val) => print(val)); Future(() { compute(comFunc, '6'); return '6處理'; }).then((val) => print(val)); Future(() { compute(comFunc, '7'); scheduleMicrotask(() { print('8處理'); //2 }); return '7處理'; }).then((val) => print(val)); } /* * computeTest是Isolate的高層封裝 * */ void computeTest() { //創建Port ReceivePort port = ReceivePort(); compute(comFunc, '初始值').then((val) => print(val)); } String comFunc(str) { return '${str}處理'; } /* * Isolate 多線程 * Isolate 看起來更加像進程.因為有獨立的內存空間! * ReceivePort如果使用到變量,變量是進行深拷貝的值拷貝.內部修改值並不會影響外部變量本身,不用擔心多線程的資源搶奪問題!不需要鎖! * */ Future<void> IsolateTest() async { //創建Port ReceivePort port = ReceivePort(); //創建isolate Isolate iso = await Isolate.spawn(isoFunc, port.sendPort); port.listen((val) { print('內部a=$a'); a = val; port.close(); iso.kill(); }); sleep(Duration(seconds: 1)); print('外部a=$a'); } int a = 1; void isoFunc(SendPort port) { sleep(Duration(seconds: 1)); a = 200; print(port); port.send(100); } //開始 , 5, 3,6,8,7,1,4,10,2,9 void testFuture4() { Future x1 = Future(() => null); x1.then((value) { print('6'); scheduleMicrotask(() => print('7')); }).then((value) => print('8')); Future x = Future(() => print('1')); x.then((value) { print('4'); Future(() => print('9')); }).then((value) => print('10')); Future(() => print('2')); scheduleMicrotask(() => print('3')); print('5'); } /* * scheduleMicrotask微任務 * 在同一方法體中微任務優先級高於Future, * */ void MicrotTest() { print('進入'); Future(() { print('A'); scheduleMicrotask(() { print('A ---- scheduleMicroTask'); }); return Future(() => print('A--Future')); }).then((value) => print('A結束')); scheduleMicrotask(() { print('scheduleMicroTask'); }); } /* * Future異步組 * 在組中是按照同步執行 * */ void FutureGroup() { print('進入'); Future.wait([ Future.sync(() { sleep(Duration(seconds: 2)); print('結束1'); return '任務一'; }), Future.sync(() { sleep(Duration(seconds: 1)); print('結束2'); return '任務二'; }), ]).then((value) { print(value); }); print('結束'); } /* * Future也可以同步執行 * */ void FutureSync() { print('進入'); Future.sync(() { sleep(Duration(seconds: 1)); print('異步操作'); }); print('結束'); } //future的優先級比then低 /* * Future執行完之后, 會將涉及到的所有then進行一次性添加 * */ void FutureThenOrder() { Future(() { sleep(Duration(seconds: 1)); return '第一個異步處理'; }).then((e) { print(e); return Future(() { sleep(Duration(seconds: 1)); return '第二個異步處理'; }); }).then((e) { print(e); return '第一個異步處理2'; }); } /* * 1,FutureOr<T>表示可以返回一個Future對象或者是<T>實例 * 2,catchError在then前時,無法阻止then的執行,因為當前then實際上是在捕獲catcherror這個Future * 3, catchError的閉包返回值是依附上一層的<T>,如果上一層沒有返回值,catcherror中的返回值需要單聲明變量后使用 * 4, 超時 * */ Future<void> throwError() async { print('進入'); Future future = Future(() { //1 print('異步操作'); sleep(Duration(seconds: 1)); // return '異步完成'; throw Exception('出錯了'); }) .then((val) { print('第一次then:${val}'); return '第一次then結束'; }) .catchError((e) { print('errpr:${e}'); return '錯誤處理'; //3 }) .then((e) => print(e)) //2 .timeout(Duration(seconds: 1)); //4 print('結束'); } /* * 1,使用await以后下方所有代碼都將進行同步執行 * 2,async/await必須成對出現,使用async進行方法的修飾后返回值必須也是Future<T> * */ Future<void> getParams1() async { print('進入'); await Future(() { sleep(Duration(seconds: 1)); print('異步操作'); }); print('結束'); }
正文
1:Dart的Event Loop
Dart的事件循環如下圖所示。和JavaScript的基本一樣。循環中有兩個隊列。一個是微任務隊列(MicroTask queue),一個是事件隊列(Event queue)。
在Dart中,實際上有兩種隊列:
-
事件隊列(event queue),包含所有的外來事件:I/O、mouse events、drawing events、timers、isolate之間的信息傳遞。
-
微任務隊列(microtask queue),表示一個短時間內就會完成的異步任務。它的優先級最高,高於event queue,只要隊列中還有任務,就可以一直霸占着事件循環。microtask queue添加的任務主要是由 Dart內部產生。
在每一次事件循環中,Dart總是先去第一個microtask queue中查詢是否有可執行的任務,如果沒有,才會處理后續的event queue的流程。

Dart的事件循環的運行遵循以下規則:
- 首先處理所有微任務隊列里的微任務。
- 處理完所有微任務以后。從事件隊列里取1個事件進行處理。
- 回到微任務隊列繼續循環。
注意第一步里的所有,也就是說在處理事件隊列之前,Dart要先把所有的微任務處理完。如果某一時刻微任務隊列里有8個微任務,事件隊列有2個事件,Dart也會先把這8個微任務全部處理完再從事件隊列中取出1個事件處理,之后又會回到微任務隊列去看有沒有未執行的微任務。
總而言之,就是對微任務隊列是一次性全部處理,對於事件隊列是一次只處理一個。
這個流程要清楚,清楚了才能理解Dart代碼的執行順序。
正常情況下,一個 Future 異步任務的執行是相對簡單的:
- 聲明一個 Future 時,Dart 會將異步任務的函數執行體放入event queue,然后立即返回,后續的代碼繼續同步執行。
- 當同步執行的代碼執行完畢后,event queue會按照加入event queue的順序(即聲明順序),依次取出事件,最后同步執行 Future 的函數體及后續的操作。
2:Future用法
0:timer
Timer.run((){ print('a event'); });
1:Future+async
String _name = "dingding"; void main() { print("開始_name = ${_name}"); getData_async_future(); print("結束_name = ${_name}"); } //async 標記是一個異步函數 void getData_async_future() async { Future future = Future(() { //Futuren內部放耗時操作 for (int i = 0; i < 10000; i++) { _name = "lala"; } print("耗時操作"); return _name; print("耗時結束_name = ${_name}"); }); //使用then接收Future 中的回調 future.then((value) { print(value); print("耗時結束_name = ${_name}"); }); }
打印
flutter: 開始_name = dingding flutter: 結束_name = dingding flutter: 耗時操作 flutter: lala flutter: 耗時結束_name = lala
可以看到結束操作早於耗時操作,說明異步處理了耗時操作。
2:async 搭配await 實現同步執行
await:后面跟着一個Future,表示等待該異步任務完成,異步任務完成后才會繼續往下執行。await只能出現在異步函數內部。能夠讓我們可以像寫同步代碼那樣來執行異步任務而不使用回調的方式。
注意點
- 后面的操作必須是異步才能用awati
- 當前函數必須是異步函數
void getData_async_awati() async { print("start"); Future future = await Future(() { for (int i = 0; i < 10000; i++) { } print('耗時操作'); }); print("end"); } // 打印 // flutter: start // flutter: 耗時操作 // flutter: end
3:then、error、whenComplete 鏈式語法
- then:在Future內部函數處理完后就會有回調
- error:在Future內部拋出錯誤時,會有回調
- whenComplete:不管Future內部是否拋出錯誤,都會有回調 eg: 寫法一:直接在then函數中取onError函數回調
void getData_async_future_error() async { //鏈式語法推薦 Future(() { for (int i = 0; i < 10000; i++) { } throw Exception("網絡異常"); }).then((value) { print('then 來了'); print(value); },onError: (error) { print('捕獲了錯誤 error:${error.toString()}'); }).whenComplete(() => print('完成了')); //非鏈式語法 不推薦 Future future = Future(() { for (int i = 0; i < 10000; i++) { } throw Exception("網絡異常"); }); future.then((value) { print('then 來了'); print(value); },onError: (error) { print('捕獲了錯誤 error:${error.toString()}'); }); future.whenComplete(() => print('完成了')); }
打印:
flutter: 捕獲了錯誤 error:Exception: 網絡異常
flutter: 完成了
面的then、whenComplete都是用鏈式語法,推薦使用這中方式
方式二:使用catchError
Future(() { for (int i = 0; i < 10000; i++) { } throw Exception("網絡異常"); }).then((value) { print('then 來了'); print(value); }).catchError((error) {//處理錯誤 print('捕獲了錯誤 error:${error.toString()}'); }).whenComplete(() => print('完成了'));
4:future執行順序
//執行順序按照編寫順序,類似隊列
//then比Future優先級高
void futureOrder() { //執行順序按照編寫順序,類似隊列 //then比Future優先級高 Future(() { sleep(Duration(seconds: 1)); return '任務一'; }).then((value) { print('$value 結束'); return "任務五"; }).then((value) => print(value)); Future(() { return '任務二'; }).then((value) => print('$value 結束')); Future(() { return '任務三'; }).then((value) => print('$value 結束')); Future(() { return '任務四'; }).then((value) => print('$value 結束')); print('添加完畢'); }
打印
flutter: 開始_name = dingding flutter: 添加完畢 flutter: 結束_name = dingding flutter: 任務一 結束 flutter: 任務五 flutter: 任務二 結束 flutter: 任務三 結束 flutter: 任務四 結束
5:依賴
void futureDepandense() { Future.wait([ Future(() { print('任務一'); return "任務一"; }), Future(() { sleep(Duration(seconds: 1)); print('任務二'); return "任務二"; }), ]).then((value) {//注意此時value 就是一個數組 print('then:來了:${value[0]} + ${value[1]}'); print('任務3'); }); }
打印
flutter: 任務一 flutter: 任務二 flutter: then:來了:任務一 + 任務一 flutter: 任務3
6:scheduleMicrotask 微任務
- scheduleMicrotask 比Future異步任務高
- Future 在事件隊列里面
- 在同級中,then 優先級 < scheduleMicrotask
- 在then函數中,then 優先級 > scheduleMicrotask
void dartLoop() { print('out task 1'); Future(() => print("A")).then((value) => print("A 結束")); Future(() => print("B")).then((value) => print("B 結束")); scheduleMicrotask(() { print('Microtask 1'); }); print('out task 2'); }
打印:
flutter: out task 1 flutter: out task 2 flutter: Microtask 1 flutter: A flutter: A 結束 flutter: B flutter: B 結束
7:多線程:Isolate
Dart中一般使用Isolate
實現多線程。Isolate
有獨立的內存空間,不用考慮多線程時資源搶奪問題,即不需要鎖。所以Isolate更像多進程操作。但是進程之間的通信也就相對麻煩,其使用port通信。
int a = 10; Future<void> dartIsolate() async { print('開始'); print("外部 a = ${a}"); Isolate.spawn(funcA,100); //創建Port ReceivePort port = ReceivePort(); //創建Isolate Isolate ISO = await Isolate.spawn(funcB,port.sendPort); port.listen((message) { print("portMessage ${message}"); a = message; //關閉端口 port.close(); //銷毀ISO ISO.kill(); }); print('結束'); print("外部 a = ${a}"); } void funcA(int p) { a = p; print('funcA'); print("內部 a = ${a}");//a = 100 說明兩個a不在同一片內存空間,間接說明 Isolate其實是開辟一個新的進程 } void funcB(SendPort sendPort) { print('funcB'); sendPort.send(1000); }
打印:
flutter: 開始_name = dingding flutter: 開始 flutter: 外部 a = 10 flutter: 結束_name = dingding flutter: funcA flutter: 內部 a = 100 flutter: funcB flutter: 結束 flutter: 外部 a = 10 flutter: portMessage 1000
8:更輕的compute
compute 是對Isolate的封裝,更輕量的。線程通信更加方便
//compute 是對Isolate的封裝,更輕量的。線程通信更加方便 void computeTest() async { print("start"); //compute能直接拿到回調,不需要做多余的進程端口通信 int x = await compute(funcC,10); print('x = ${x}'); print("end"); } int funcC(int p) { print('funcC'); return p + 100; }
打印:
flutter: start flutter: funcC flutter: x = 110 flutter: end
面試題
下面打印順序是什么
void dartLoopTest() { Future x0 = Future(() => null); Future x = Future(() => print('1')); Future(() => print('2')); scheduleMicrotask(() => print('3')); x.then((value) { print('4'); Future(() => print('5')); }).then((value) => print('6')); print('7'); x0.then((value) { print('8'); scheduleMicrotask(() { print('9'); }); }).then((value) => print('10')); }
打印:
flutter: 7 flutter: 3 flutter: 8 flutter: 10 flutter: 9 flutter: 1 flutter: 4 flutter: 6 flutter: 2 flutter: 5
注意
引用
1:Dart中的異步編程——Future、async和await