1.簡(jiǎn)介:
??Dart庫(kù)充滿了返回Future或Stream對(duì)象的函數(shù);這些函數(shù)都是異步的。這些函數(shù)在做一些可能耗時(shí)的操作(如I/O)時(shí),會(huì)立即返回一個(gè)Future或Stream對(duì)象,進(jìn)行函數(shù)后面代碼的執(zhí)行,而不需要等待該操作完成后再執(zhí)行函數(shù)后面的代碼。
??Dart中使用async``await關(guān)鍵字來(lái)實(shí)現(xiàn)異步編程,但是和Java、OC等異步不同,代碼類似于同步代碼,實(shí)際上是異步執(zhí)行的。
關(guān)鍵術(shù)語(yǔ):
同步操作:同步操作阻止其他操作執(zhí)行,直到它完成。
同步函數(shù):同步函數(shù)只執(zhí)行同步操作。
異步操作:異步操作一旦啟動(dòng),就允許其他操作在它完成之前執(zhí)行。
異步函數(shù):一個(gè)異步函數(shù)至少執(zhí)行一個(gè)異步操作,也可以執(zhí)行同步操作。
異步場(chǎng)景:
- 獲取網(wǎng)絡(luò)數(shù)據(jù)(請(qǐng)求數(shù)據(jù)、圖片下載、文件下載等);
- 讀取數(shù)據(jù)(從文件、數(shù)據(jù)庫(kù)、磁盤(pán)中);
- 寫(xiě)入數(shù)據(jù)(文件、數(shù)據(jù)庫(kù)、磁盤(pán)等)。
2.處理Future:
??Future表示一個(gè)不會(huì)立即完成的計(jì)算過(guò)程。與普通函數(shù)直接返回結(jié)果不同的是異步函數(shù)返回一個(gè)將會(huì)包含結(jié)果的Future。該 Future會(huì)在結(jié)果準(zhǔn)備好時(shí)通知調(diào)用者。
future與Future:
??future(小寫(xiě)“f”)是Future類的一個(gè)實(shí)例。future表示異步操作的結(jié)果,可以有兩種狀態(tài):未完成uncompleted或已完成completed。
未完成uncompleted:
??當(dāng)調(diào)用一個(gè)異步函數(shù)時(shí),它將返回一個(gè)未完成的future。該future表示函數(shù)正在等待函數(shù)的異步操作完成或拋出錯(cuò)誤。
成功的完成Completing with a value:
??Future <T>類型的future以T類型的值結(jié)束。例如,Future <String>類型的future產(chǎn)生一個(gè)字符串值。如果future沒(méi)有產(chǎn)生一個(gè)可用的值,那么future的類型是Future <void>。
錯(cuò)誤的完成Completing with an error:
??如果函數(shù)執(zhí)行的異步操作由于任何原因失敗,則future函數(shù)將以錯(cuò)誤的方式完成。
// 引入Future,并返回一個(gè)Future<void>的例子
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info from another service or database.
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}
// 返回一個(gè)future錯(cuò)誤的例子
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info but encounters a bug
return Future.delayed(const Duration(seconds: 2),
() => throw Exception('Logout failed: user ID is invalid'));
}
Futuer和future總結(jié):
-
Future<T>實(shí)例產(chǎn)生類型T的值; - 如果
future沒(méi)有產(chǎn)生一個(gè)可用的值,那么future的類型是future <void>; -
future有兩種狀態(tài):未完成或已完成; - 當(dāng)調(diào)用一個(gè)返回
future的函數(shù)時(shí),該函數(shù)將會(huì)進(jìn)入一個(gè)異步任務(wù)隊(duì)列中queues,并立即返回一個(gè)未完成狀態(tài)的future; - 當(dāng)
future的操作完成時(shí),它會(huì)返回一個(gè)T類型的值或者一個(gè)錯(cuò)誤。
3.異步async和等待await:
??async和await關(guān)鍵字提供了一種聲明性的方式來(lái)定義異步函數(shù)并使用它們的結(jié)果;使用的基本原則:
- 要定義一個(gè)異步函數(shù),請(qǐng)?jiān)诤瘮?shù)體前添加
async; - await關(guān)鍵字只在異步函數(shù)中有效;
- 同步函數(shù)中調(diào)用異步函數(shù),處理異步函數(shù)結(jié)果需要
.then語(yǔ)法。
Future<String> createOrderMessage() async {
print("message star");
var order = await fetchUserOrder3();
print("message return");
return 'Your order is: $order';
}
Future<String> fetchUserOrder3() async {
// Imagine that this function is more complex and slow.
print("order star");
var result = await Future.delayed(
const Duration(seconds: 2), () {
return 'Large Latte';
});
print("order return");
return result;
}
// 同步函數(shù)中調(diào)用
print('message top');
createOrderMessage().then((value) {
print(value);
});
print('message bottom');
// 打印順序
flutter: message top
flutter: message star
flutter: order star
flutter: message bottom
flutter: order return
flutter: message return
flutter: Your order is: Large Latte
??如何解讀上述代碼的打印順序呢?
- 同步函數(shù)中代碼是從上往下順序執(zhí)行的;
- 異步函數(shù)中,可能是全部同步執(zhí)行,也可能有同步和也有異步執(zhí)行;
- 異步函數(shù)中,以
await關(guān)鍵字為分界線,await以上包含await調(diào)用函數(shù)的語(yǔ)句是同步的,await的返回結(jié)果和后面的代碼是異步執(zhí)行。
示例如圖,紅色的代碼為同步執(zhí)行,綠色代碼為異步執(zhí)行的:
異步函數(shù)的執(zhí)行.png
??開(kāi)始解讀:
- 同步函數(shù)首先打?。簃essage top;
- 同步調(diào)用異步函數(shù)createOrderMessage,進(jìn)入該函數(shù);
- 在函數(shù)createOrderMessage中,首先同步打?。簃essage star;
- 同步調(diào)用
await fetchUserOrder3();,進(jìn)入函數(shù)fetchUserOrder3; - 在函數(shù)fetchUserOrder3中,首先同步打?。簅rder star;
- 同步調(diào)用
await Future.delayed,進(jìn)行函數(shù)Future.delayed調(diào)用; - 函數(shù)Future.delayed延遲執(zhí)行,為異步調(diào)用,直接返回一個(gè)Future<String>的實(shí)例future,并加入異步隊(duì)列中等待執(zhí)行;
- 函數(shù)fetchUserOrder3檢測(cè)到異步執(zhí)行,直接返回一個(gè)future,退出同步執(zhí)行,將異步部分加入異步隊(duì)列中等待執(zhí)行;
- 函數(shù)createOrderMessage與6相同;
- 同步函數(shù)中異步函數(shù)createOrderMessage的同步部分執(zhí)行完畢,繼續(xù)執(zhí)行下面同步代碼,打?。簃essage bottom;
- 隊(duì)列要求先進(jìn)先出,當(dāng)最先加入異步隊(duì)列的任務(wù)已經(jīng)完成后,會(huì)以加入的次序執(zhí)行后續(xù)加入的異步代碼,后面的打印書(shū)序?yàn)椋?br>
order return
message return
Your order is: Large Latte
3.異步錯(cuò)誤error處理:
??在異步函數(shù)中,如同同步函數(shù)一樣使用try-catch處理出現(xiàn)的錯(cuò)誤或者異常。
Future<void> printOrderMessage() async {
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
print(order);
} catch (err) {
print('Caught error: $err');
}
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex.
var str = Future.delayed(
const Duration(seconds: 4),
() => throw 'Cannot locate user order');
return str;
}
Future<void> main() async {
// 會(huì)拋出Cannot locate user order異常
await printOrderMessage();
}
4.處理流Streams:
官方文檔
??Stream 是一系列異步事件的序列。其類似于一個(gè)異步的 Iterable,不同的是當(dāng)你向 Iterable 獲取下一個(gè)事件時(shí)它會(huì)立即給你,但是 Stream 則不會(huì)立即給你而是在它準(zhǔn)備好時(shí)告訴你。
??Stream概要:
-
Stream提供一個(gè)異步的數(shù)據(jù)序列。 - 數(shù)據(jù)序列包括用戶生成的事件和從文件讀取的數(shù)據(jù)。
- 你可以使用
Stream API中的listen()方法和await for關(guān)鍵字來(lái)處理一個(gè)Stream。 - 當(dāng)出現(xiàn)錯(cuò)誤時(shí),
Stream提供一種處理錯(cuò)誤的方式。 - Stream 有兩種類型:
Single-Subscription和Broadcast。
4.1接收Stream事件:
??Stream可以通過(guò)許多方式創(chuàng)建,而這些所有的創(chuàng)建方式都可以相同的方式在代碼中使用:像使用for循環(huán) 迭代一個(gè)Iterable一樣,我們可以使用 異步for循環(huán) (通常我們直接稱之為await for)來(lái)迭代Stream中的事件。例如:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
??上面代碼只是簡(jiǎn)單地接收整型事件流中的每一個(gè)事件并將它們相加,然后返回(被Future包裹)相加后的整型值。當(dāng)循環(huán)體結(jié)束時(shí),函數(shù)會(huì)暫停直到下一個(gè)事件到達(dá)或Stream完成。內(nèi)部使用await for循環(huán)的函數(shù)需要使用async關(guān)鍵字標(biāo)記。
下面的示例中使用了async*函數(shù)生成一個(gè)簡(jiǎn)單的整型Stream來(lái)測(cè)試上一個(gè)代碼片段:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
// 調(diào)用
var stream = countStream(10);
var sum = sumStream(stream);
sum.then((value) {
print(value);
});
4.2Stream錯(cuò)誤處理:
??當(dāng)Stream再也沒(méi)有需要處理的事件時(shí)會(huì)變?yōu)橥瓿蔂顟B(tài)。與此同時(shí),調(diào)用者可以像接收到新事件回調(diào)那樣接收Stream完成的事件回調(diào)。當(dāng)使用await for循環(huán)讀取事件時(shí),循環(huán)會(huì)在Stream完成時(shí)停止。
??有時(shí)在Stream完成前會(huì)出現(xiàn)錯(cuò)誤;比如從遠(yuǎn)程服務(wù)器獲取文件時(shí)出現(xiàn)網(wǎng)絡(luò)請(qǐng)求失敗,或者創(chuàng)建事件時(shí)出現(xiàn) bug;在出現(xiàn)錯(cuò)誤時(shí),需要將錯(cuò)誤告知使用者。
??Stream可以像提供數(shù)據(jù)事件那樣提供錯(cuò)誤事件。大多數(shù)Stream會(huì)在第一次錯(cuò)誤出現(xiàn)后停止,但其也可以提供多次錯(cuò)誤并可以在在出現(xiàn)錯(cuò)誤后繼續(xù)提供數(shù)據(jù)事件。在本篇文檔中我們只討論Stream最多出現(xiàn)并提供一次錯(cuò)誤事件的情況。
??當(dāng)使用await for讀取Stream時(shí),如果出現(xiàn)錯(cuò)誤,則由循環(huán)語(yǔ)句拋出,同時(shí)循環(huán)結(jié)束。你可以使用try-catch語(yǔ)句捕獲錯(cuò)誤。下面的示例會(huì)在循環(huán)迭代到參數(shù)值等于 4 時(shí)拋出一個(gè)錯(cuò)誤:
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
if (i == 4) {
throw Exception('Intentional exception');
} else {
yield i;
}
}
}
5.隔離區(qū)Isolates:
??大多數(shù)計(jì)算機(jī)中,甚至在移動(dòng)平臺(tái)上,都在使用多核 CPU。為了有效利用多核性能,開(kāi)發(fā)者一般使用共享內(nèi)存的方式讓線程并發(fā)地運(yùn)行。然而,多線程共享數(shù)據(jù)通常會(huì)導(dǎo)致很多潛在的問(wèn)題,并導(dǎo)致代碼運(yùn)行出錯(cuò)。
??為了解決多線程帶來(lái)的并發(fā)問(wèn)題,Dart 使用isolate替代線程,所有的 Dart 代碼均運(yùn)行在一個(gè) isolate 中。每一個(gè)isolate有它自己的堆內(nèi)存以確保其狀態(tài)不被其它 isolate 訪問(wèn)。
??所有的 Dart 代碼都是在一個(gè)isolate中運(yùn)行,而非線程。每個(gè) isolate 都有一個(gè)單獨(dú)的執(zhí)行線程,并且不與其他的isolate共享任何可變對(duì)象。
你可以查閱下面的文檔獲取更多相關(guān)信息:
- Dart 中的并發(fā)特性
- dart:isolate API 參考 介紹了 Isolate.spawn() 和 TransferableTypedData 的用法
- Flutter 文檔上關(guān)于 后臺(tái)解析 的實(shí)用教程
- Isolate sample app
