前言
我們?cè)陂_(kāi)發(fā)flutter應(yīng)用的時(shí)候編寫(xiě)代碼起宽,要么是同步代碼坯沪,要么是異步代碼腐晾。那么什么是同步什么是異步呢藻糖?
- 同步代碼就是正常編寫(xiě)的代碼塊
- 異步代碼就是Future库车,async等關(guān)鍵字修飾的代碼塊
一柠衍、時(shí)機(jī)不同
他們區(qū)別于運(yùn)行時(shí)機(jī)不同,同步代碼先執(zhí)行牺勾,異步代碼后執(zhí)行禽最,即使你的同步代碼寫(xiě)在最后川无,那也是你的同步代碼執(zhí)行虑乖,之后運(yùn)行你的異步代碼疹味。
二糙捺、機(jī)制不同
異步代碼運(yùn)行在 event loop中洪灯,類(lèi)似于A(yíng)ndroid里的Looper機(jī)制,是一個(gè)死循環(huán)掏呼,event loop不斷的從事件隊(duì)列里取事件然后運(yùn)行憎夷。
event loop循環(huán)機(jī)制
如圖所示昧旨,事件存放于隊(duì)列中兔沃,loop循環(huán)執(zhí)行
Dart的事件循環(huán)如下圖所示粘拾。循環(huán)中有兩個(gè)隊(duì)列。一個(gè)是微任務(wù)隊(duì)列(MicroTask queue)入偷,一個(gè)是事件隊(duì)列(Event queue)疏之。
事件隊(duì)列包含外部事件锋爪,例如I/O, Timer其骄,繪制事件等等。
微任務(wù)隊(duì)列則包含有Dart內(nèi)部的微任務(wù)索抓,主要是通過(guò)scheduleMicrotask來(lái)調(diào)度逼肯。
- 首先處理所有微任務(wù)隊(duì)列里的微任務(wù)篮幢。
- 處理完所有微任務(wù)以后三椿。從事件隊(duì)列里取1個(gè)事件進(jìn)行處理赋续。
- 回到微任務(wù)隊(duì)列繼續(xù)循環(huán)纽乱。
Dart要先把所有的微任務(wù)處理完鸦列,再處理一個(gè)事件薯嗤,處理完之后再看看微任務(wù)隊(duì)列纤泵。如此循環(huán)玻褪。
例子:
8個(gè)微任務(wù)
2個(gè)事件
Dart-->執(zhí)行完8個(gè)微任務(wù)
Dart-->執(zhí)行完1個(gè)事件
Dart-->查看微任務(wù)隊(duì)列
Dart-->再執(zhí)行完1個(gè)事件
done
異步執(zhí)行
那么在Dart中如何讓你的代碼異步執(zhí)行呢公荧?很簡(jiǎn)單窟社,把要異步執(zhí)行的代碼放在微任務(wù)隊(duì)列或者事件隊(duì)列里就行了券勺。
可以調(diào)用scheduleMicrotask來(lái)讓代碼以微任務(wù)的方式異步執(zhí)行
scheduleMicrotask((){
print('a microtask');
});
可以調(diào)用Timer.run來(lái)讓代碼以Event的方式異步執(zhí)行
Timer.run((){
print('a event');
});
Future異步執(zhí)行
創(chuàng)建一個(gè)立刻在事件隊(duì)列里運(yùn)行的Future:
Future(() => print('立刻在Event queue中運(yùn)行的Future'));
創(chuàng)建一個(gè)延時(shí)1秒在事件隊(duì)列里運(yùn)行的Future:
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中運(yùn)行的Future'));
創(chuàng)建一個(gè)在微任務(wù)隊(duì)列里運(yùn)行的Future:
Future.microtask(() => print('在Microtask queue里運(yùn)行的Future'));
創(chuàng)建一個(gè)同步運(yùn)行的Future:
Future.sync(() => print('同步運(yùn)行的Future'));
這里要注意一下,這個(gè)同步運(yùn)行指的是構(gòu)造Future的時(shí)候傳入的函數(shù)是同步運(yùn)行的盗扒,這個(gè)Future通過(guò)then串進(jìn)來(lái)的回調(diào)函數(shù)是調(diào)度到微任務(wù)隊(duì)列異步執(zhí)行的。
有了Future之后, 通過(guò)調(diào)用then來(lái)把回調(diào)函數(shù)串起來(lái)缕碎,這樣就解決了"回調(diào)地獄"的問(wèn)題咏雌。
Future(()=> print('task'))
.then((_)=> print('callback1'))
.then((_)=> print('callback2'));
在task打印完畢以后赊抖,通過(guò)then串起來(lái)的回調(diào)函數(shù)會(huì)按照鏈接的順序依次執(zhí)行氛雪。
如果task執(zhí)行出錯(cuò)怎么辦耸成?你可以通過(guò)catchError來(lái)鏈上一個(gè)錯(cuò)誤處理函數(shù):
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'));
上面這個(gè)Future執(zhí)行時(shí)直接拋出一個(gè)異常报亩,這個(gè)異常會(huì)被catchError捕捉到。類(lèi)似于Java中的try/catch機(jī)制的catch代碼塊井氢。運(yùn)行后只會(huì)執(zhí)行catchError里的代碼弦追。兩個(gè)then中的代碼都不會(huì)被執(zhí)行。
既然有了類(lèi)似Java的try/catch花竞,那么Java中的finally也應(yīng)該有吧劲件。有的,那就是whenComplete:
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'))
.whenComplete(()=> print('whenComplete'));
無(wú)論這個(gè)Future是正常執(zhí)行完畢還是拋出異常约急,whenComplete都一定會(huì)被執(zhí)行零远。
結(jié)果執(zhí)行
把如上的代碼在dart中運(yùn)行看看輸出
print('1');
var fu1 = Future(() => print('立刻在Event queue中運(yùn)行的Future'));
Future future2 = new Future((){
print("future2 初始化任務(wù)");
});
print('2');
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中運(yùn)行的Future'));
print('3');
var fu2 = Future.microtask(() => print('在Microtask queue里運(yùn)行的Future'));
print('4');
Future.sync(() => print('同步運(yùn)行的Future')).then((value) => print('then同步運(yùn)行的Future'));
print('5');
fu1.then((value) => print('then 立刻在Event queue中運(yùn)行的Future'));
print('6');
fu2.then((value) => print('then 在Microtask queue里運(yùn)行的Future'));
print('7');
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'));
print('8');
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'))
.whenComplete(()=> print('whenComplete'));
print('9');
Future future4 = Future.value("立即執(zhí)行").then((value){
print("future4 執(zhí)行then");
}).whenComplete((){
print("future4 執(zhí)行whenComplete");
});
print('10');
future2.then((_) {
print("future2 執(zhí)行then");
future4.then((_){
print("future4 執(zhí)行then2");
});
});
輸出
I/flutter (29040): 1
I/flutter (29040): 2
I/flutter (29040): 3
I/flutter (29040): 4
I/flutter (29040): 同步運(yùn)行的Future
I/flutter (29040): 5
I/flutter (29040): 6
I/flutter (29040): 7
I/flutter (29040): 8
I/flutter (29040): 9
I/flutter (29040): 10
I/flutter (29040): 在Microtask queue里運(yùn)行的Future
I/flutter (29040): then 在Microtask queue里運(yùn)行的Future
I/flutter (29040): then同步運(yùn)行的Future
I/flutter (29040): future4 執(zhí)行then
I/flutter (29040): future4 執(zhí)行whenComplete
I/flutter (29040): 立刻在Event queue中運(yùn)行的Future
I/flutter (29040): then 立刻在Event queue中運(yùn)行的Future
I/flutter (29040): future2 初始化任務(wù)
I/flutter (29040): future2 執(zhí)行then
I/flutter (29040): future4 執(zhí)行then2
I/flutter (29040): we have a problem
I/flutter (29040): we have a problem
I/flutter (29040): whenComplete
I/flutter (29040): 1秒后在Event queue中運(yùn)行的Future
輸出說(shuō)明:
- 先輸出同步代碼烤宙,再輸出異步代碼
- 通過(guò)then串聯(lián)起的任務(wù)會(huì)在主要任務(wù)執(zhí)行完立即執(zhí)行
- Future.sync是同步執(zhí)行供填,then執(zhí)行在微任務(wù)隊(duì)列中
- 通過(guò)Future.value()函數(shù)創(chuàng)建的任務(wù)是立即執(zhí)行的
- 如果是在whenComplete之后注冊(cè)的then膳帕,那么這個(gè)then的任務(wù)將放在microtask執(zhí)行
Completer
Completer允許你做某個(gè)異步事情的時(shí)候,調(diào)用c.complete(value)方法來(lái)傳入最后要返回的值。最后通過(guò)c.future的返回值來(lái)得到結(jié)果拼坎,(注意:宣告完成的complete和completeError方法只能調(diào)用一次盛龄,不然會(huì)報(bào)錯(cuò))兆沙。
例子:
test() async {
Completer c = new Completer();
for (var i = 0; i < 1000; i++) {
if (i == 900 && c.isCompleted == false) {
c.completeError('error in $i');
}
if (i == 800 && c.isCompleted == false) {
c.complete('complete in $i');
}
}
try {
String res = await c.future;
print(res); //得到complete傳入的返回值 'complete in 800'
} catch (e) {
print(e);//捕獲completeError返回的錯(cuò)誤
}
}