異常的捕獲
onError 與 catchError 的區(qū)別
getdata() async {
Future future = Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('網(wǎng)絡(luò)異常');
});
// future.then((value) => print('value=$value'),
// onError: (e)=>print(e.toString()));
future
.then((value) => print('value=$value'))
.catchError((e)=>print('使用的時(shí)候捕獲到了異常:' + e.toString()));
}
當(dāng)我們?cè)?Future
的閉包中拋出異常的時(shí)候可以使用 onError
或者 catchError
進(jìn)行捕獲異常并處理豪诲,它們的區(qū)別就是 onError
是寫在 then
里面的务甥,是針對(duì) then
這個(gè)事件。
catchError 的使用順序
雖然我們上面已經(jīng)通過(guò) catchError
捕獲了異常馍忽,但是當(dāng)我們后面再調(diào)用 whenComplete
的時(shí)候還需要再次調(diào)用 catchError
迅矛,要不然程序還是會(huì)拋出異常妨猩。正確代碼如下。
future
.then((value) => print('value=$value'))
.catchError((e)=>print('使用的時(shí)候捕獲到了異常:' + e.toString()));
future
.whenComplete(() => print('完成了'))
.catchError((e)=>print('完成的時(shí)候捕獲到了異常:' + e.toString()));
Future 鏈?zhǔn)秸{(diào)用
Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('網(wǎng)絡(luò)異常');
})
.then((value) => print('value=$value'))
.whenComplete(() => print('完成了'))
.catchError((e)=>print('捕獲到了異常:' + e.toString()));
針對(duì)以上代碼我們我們可以使用鏈?zhǔn)秸{(diào)用進(jìn)行簡(jiǎn)寫秽褒,但是需要注意一點(diǎn)的是 catchError
最好放到最后面壶硅。
代碼抽取
getdata() async {
Future(() {
for (int i = 0; i < 10000000; i++) {}
throw Exception('網(wǎng)絡(luò)異常');
})
.then(thenFunc)
.whenComplete(() => print('完成了'))
.catchError((e)=>print('捕獲到了異常:' + e.toString()));
}
FutureOr thenFunc(Never value) {
print('value=$value');
}
以 then
為例,當(dāng)我們閉包中代碼量比較多的時(shí)候销斟,我們可以在外面定義一個(gè)方法庐椒,對(duì)代碼進(jìn)行抽離。
多個(gè)異步任務(wù)的處理
Future 任務(wù)順序執(zhí)行
testFuture() async {
Future(() {
return '任務(wù) 1';
}).then((value) => print('value=$value'));
Future(() {
sleep(Duration(seconds: 1));
return '任務(wù) 2';
}).then((value) => print('value=$value'));
Future(() {
return '任務(wù) 3';
}).then((value) => print('value=$value'));
}
這里因?yàn)?Future
任務(wù)都是在同一個(gè)隊(duì)列中蚂踊,且 Flutter
是單線程约谈,所以這里任務(wù)的執(zhí)行順序是按照 Future
的添加順序執(zhí)行的,任務(wù)1
-> 任務(wù)2
-> 任務(wù)3
。
任務(wù)前后依賴
后面的任務(wù)依賴前一個(gè)任務(wù)的結(jié)果
Future(() {
return '任務(wù) 1';
}).then((value) {
print('$value結(jié)束');
return '任務(wù) 2';
}).then((value) {
print('$value結(jié)束');
return '任務(wù) 3';
});
當(dāng)我們后面的任務(wù)需要依賴前一個(gè)任務(wù)的執(zhí)行結(jié)果的時(shí)候棱诱,可以在 then
的閉包中執(zhí)行下一個(gè)任務(wù)泼橘,并把結(jié)果數(shù)據(jù)返回出去。
多個(gè)任務(wù)結(jié)束統(tǒng)一處理
Future.wait([
Future(() {
return '任務(wù) 1';
}),
Future(() {
return '任務(wù) 2';
}),
Future(() {
return '任務(wù) 3';
})
]).then((value) => print(value[0] + value[1] + value[2]));
當(dāng)我們碰到這樣一個(gè)需求军俊,同時(shí)請(qǐng)求多個(gè)接口侥加,在這些接口都請(qǐng)求完成的時(shí)候統(tǒng)一處理,這時(shí)候我們就可以使用 Future.wait
粪躬,Future.wait
里面是一個(gè)數(shù)組,可以放入多個(gè) Future
任務(wù)昔穴,在這些任務(wù)都執(zhí)行完畢的時(shí)候會(huì)調(diào)用 then
方法镰官,這時(shí)候 value
是一個(gè)數(shù)組,裝的是這幾個(gè) Future
任務(wù)的返回結(jié)果吗货。這里 value
數(shù)組的順序跟 Future
任務(wù)的順序是一樣的泳唠,且 Future
任務(wù)也是順序執(zhí)行的。
Dart 事件循環(huán)
通過(guò)上圖案例我們可以看到 scheduleMicrotask
中的任務(wù)會(huì)比 Future
中的任務(wù)先執(zhí)行宙搬,這里是因?yàn)樵?Dart
中有兩種隊(duì)列笨腥,事件隊(duì)列跟微任務(wù)隊(duì)列。
- 事件隊(duì)列(
event queue
)勇垛,包含所有的外來(lái)事件:I/O
脖母、mouse events
、drawing events
闲孤、timers
谆级、isolate
之間的信息傳遞。 - 微任務(wù)隊(duì)列(
microtask queue
)讼积,表示一個(gè)短時(shí)間內(nèi)就會(huì)完成的異步任務(wù)肥照。它的優(yōu)先級(jí)最高,高于event queue
勤众,只要隊(duì)列中還有任務(wù)舆绎,就可以一直霸占著事件循環(huán)。microtask queue
添加的任務(wù)主要是由Dart
內(nèi)部產(chǎn)生们颜。
因?yàn)?
microtask queue
的優(yōu)先級(jí)高于event queue
吕朵,所以如果microtask queue
有太多的微任務(wù), 那么就可能會(huì)霸占住當(dāng)前的event loop
掌桩。從而對(duì)event queue
中的觸摸边锁、繪制等外部事件造成阻塞卡頓。
在每一次事件循環(huán)中波岛,Dart
總是先去 microtask queue
中查詢是否有可執(zhí)行的任務(wù)茅坛,如果沒(méi)有,才會(huì)處理后續(xù)的 event queue
中的任務(wù)。
相關(guān)案例
- 案例 1
Future f = Future(() => print('1'));
Future(() => print('2'));
scheduleMicrotask(() => print('3'));
f.then((value) => print('4'));
print('5');
這里打印的順序是 5贡蓖、3曹鸠、1、4斥铺、2彻桃。
- 案例 2
Future f = Future(() => print('1'));
Future(() => print('2'));
scheduleMicrotask(() => print('3'));
f.then((value) {
print('4');
scheduleMicrotask(() => print('5'));
}).then((value) => print('6'));
print('7');
這里打印的順序是 7、3晾蜘、1邻眷、4、6剔交、5肆饶、2,這里 6 相當(dāng)于 f.then
里面的任務(wù)岖常, then
中的任務(wù)相當(dāng)于被添加到了微任務(wù)隊(duì)列驯镊,所以 4、6 在 5 之前執(zhí)行竭鞍。
Dart 中的多線程 Isolate
Isolate.spawn(func1, 10);
Isolate.spawn(func2, 20);
Isolate.spawn(func3, 30);
如上代碼中 func1
板惑、func2
、func3
會(huì)在子線程中執(zhí)行偎快,且是無(wú)序的冯乘。Dart
中 Isolate
更像是一個(gè)進(jìn)程,它有獨(dú)立的內(nèi)存空間滨砍,也就意味著每個(gè)進(jìn)程中的空間是獨(dú)立的往湿,所以不存在資源搶奪的問(wèn)題,所以不需要鎖惋戏,這樣的話用起來(lái)就非常便捷领追。但是也有一些需要注意的問(wèn)題,數(shù)據(jù)不能直接訪問(wèn)响逢,下面我們來(lái)看一下绒窑。
這里我們?cè)?func
中對(duì) a
的值進(jìn)行了修改,但是第二次打印的時(shí)候可以看到 a
的值還是 10舔亭。因?yàn)?func
中的 a
被獨(dú)立起來(lái)了些膨,與外部相互之間不能共享。如果想讓 func
中 a
的值的修改能在外部起作用的話就需要用到端口钦铺。
int a = 10;
IsolateDemo() async {
//創(chuàng)建 port
ReceivePort port = ReceivePort();
//創(chuàng)建 Isolate
Isolate iso = await Isolate.spawn(func, port.sendPort);
//通過(guò) port 監(jiān)聽數(shù)據(jù)變化
port.listen((message) {
a = message;
print('接收到了:a = $a');
//關(guān)閉端口
port.close();
iso.kill();
});
}
func(SendPort send) {
send.send(100);
print('第一次打佣┪怼:a = $a');
}
這里我們可以定義一個(gè) port
,在 func
中傳入 port.sendPort
矛洞,這時(shí)候在 func
方法中發(fā)送數(shù)據(jù)洼哎,在 listen
閉包中就可以監(jiān)聽到。端口使用完畢后要調(diào)用 close
關(guān)閉端口,調(diào)用 kill
銷毀 Isolate
噩峦。
computeDemo() async {
int a = await compute(func1, 10);
}
int func1(int count) {
return 100;
}
Dart
中使用多線程除了 Isolate
锭沟,還有 compute
,compute
是基于 Isolate
的封裝识补,用法與 Isolate
類似族淮,有區(qū)別的就是 compute
可以接收函數(shù)中的返回值。
擴(kuò)展
pubspec.yaml 文件介紹
-
name
:項(xiàng)目名稱凭涂,必填字段祝辣。 -
description
:項(xiàng)目描述,非必填字段切油。 -
publish_to
:代表要發(fā)布的平臺(tái)较幌,none
的話代表不發(fā)布。 -
version
:工程的版本號(hào)白翻。 -
dependencies
:可以設(shè)置flutter
的版本,默認(rèn)是獲取最新版本绢片。 -
environment
:可以指定dart
版本的兼容范圍滤馍。 -
dev_dependencies
:代表開發(fā)環(huán)境下的指定版本,打包的時(shí)候不會(huì)被打包底循。 -
flutter
:字體以及圖片都是在flutter
下面設(shè)置巢株。 -
dio: ^4.0.1
: 以dio
三方庫(kù)為例,^4.0.1
代表大版本區(qū)間不變的寫法熙涤,相當(dāng)于>= 4.0.1, < 5.0.0
阁苞。dio: 4.0.1
代表指定版本,dio: any
代表任意版本祠挫,dio:
>3.0.1`` 代表版本號(hào)大于 3.0.1那槽。
import 介紹
import 'package:http/http.dart' as http;
以 http
為例,當(dāng)我們導(dǎo)入 import
的時(shí)候這里可以看到 as
等舔,as
的作用就是給庫(kù)起別名骚灸,防止類名或者方法名沖突。導(dǎo)入庫(kù)的時(shí)候默認(rèn)是整個(gè)文件都導(dǎo)入慌植,關(guān)鍵字 show
代表只要導(dǎo)入的內(nèi)容甚牲,hide
代表不需要導(dǎo)入的內(nèi)容,我們可以根據(jù)需要進(jìn)行指定蝶柿。