1.簡介:
??Dart庫充滿了返回Future或Stream對象的函數(shù);這些函數(shù)都是異步的闸迷。這些函數(shù)在做一些可能耗時的操作(如I/O)時嵌纲,會立即返回一個Future或Stream對象,進行函數(shù)后面代碼的執(zhí)行腥沽,而不需要等待該操作完成后再執(zhí)行函數(shù)后面的代碼逮走。
??Dart中使用async``await
關鍵字來實現(xiàn)異步編程,但是和Java今阳、OC等異步不同师溅,代碼類似于同步代碼,實際上是異步執(zhí)行的盾舌。
關鍵術語:
同步操作:同步操作阻止其他操作執(zhí)行墓臭,直到它完成。
同步函數(shù):同步函數(shù)只執(zhí)行同步操作妖谴。
異步操作:異步操作一旦啟動起便,就允許其他操作在它完成之前執(zhí)行。
異步函數(shù):一個異步函數(shù)至少執(zhí)行一個異步操作窖维,也可以執(zhí)行同步操作。
異步場景:
- 獲取網(wǎng)絡數(shù)據(jù)(請求數(shù)據(jù)妙痹、圖片下載铸史、文件下載等);
- 讀取數(shù)據(jù)(從文件怯伊、數(shù)據(jù)庫琳轿、磁盤中);
- 寫入數(shù)據(jù)(文件耿芹、數(shù)據(jù)庫崭篡、磁盤等)。
2.處理Future
:
??Future
表示一個不會立即完成的計算過程吧秕。與普通函數(shù)直接返回結果不同的是異步函數(shù)返回一個將會包含結果的Future
琉闪。該 Future
會在結果準備好時通知調用者。
future與Future:
??future(小寫“f”)是Future類的一個實例砸彬。future表示異步操作的結果颠毙,可以有兩種狀態(tài):未完成uncompleted
或已完成completed
斯入。
未完成uncompleted
:
??當調用一個異步函數(shù)時,它將返回一個未完成的future蛀蜜。該future表示函數(shù)正在等待函數(shù)的異步操作完成或拋出錯誤刻两。
成功的完成Completing with a value
:
??Future <T>
類型的future以T類型的值結束。例如滴某,Future <String>
類型的future產生一個字符串值磅摹。如果future沒有產生一個可用的值,那么future的類型是Future <void>
霎奢。
錯誤的完成Completing with an error
:
??如果函數(shù)執(zhí)行的異步操作由于任何原因失敗户誓,則future函數(shù)將以錯誤的方式完成。
// 引入Future椰憋,并返回一個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'));
}
// 返回一個future錯誤的例子
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總結:
-
Future<T>
實例產生類型T的值厅克; - 如果
future
沒有產生一個可用的值,那么future的類型是future <void>
橙依; -
future
有兩種狀態(tài):未完成或已完成证舟; - 當調用一個返回
future
的函數(shù)時,該函數(shù)將會進入一個異步任務隊列中queues
窗骑,并立即返回一個未完成狀態(tài)的future
女责; - 當
future
的操作完成時,它會返回一個T類型的值或者一個錯誤创译。
3.異步async
和等待await
:
??async
和await
關鍵字提供了一種聲明性的方式來定義異步函數(shù)并使用它們的結果抵知;使用的基本原則:
- 要定義一個異步函數(shù),請在函數(shù)體前添加
async
软族; - await關鍵字只在異步函數(shù)中有效刷喜;
- 同步函數(shù)中調用異步函數(shù),處理異步函數(shù)結果需要
.then
語法立砸。
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ù)中調用
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
關鍵字為分界線搁宾,await
以上包含await
調用函數(shù)的語句是同步的,await
的返回結果和后面的代碼是異步執(zhí)行倔幼。
示例如圖盖腿,紅色的代碼為同步執(zhí)行,綠色代碼為異步執(zhí)行的:
異步函數(shù)的執(zhí)行.png
??開始解讀:
- 同步函數(shù)首先打臃锊亍:message top奸忽;
- 同步調用異步函數(shù)createOrderMessage堕伪,進入該函數(shù);
- 在函數(shù)createOrderMessage中栗菜,首先同步打忧反啤:message star;
- 同步調用
await fetchUserOrder3();
疙筹,進入函數(shù)fetchUserOrder3富俄; - 在函數(shù)fetchUserOrder3中,首先同步打佣亍:order star霍比;
- 同步調用
await Future.delayed
,進行函數(shù)Future.delayed調用暴备; - 函數(shù)Future.delayed延遲執(zhí)行悠瞬,為異步調用,直接返回一個Future<String>的實例future涯捻,并加入異步隊列中等待執(zhí)行浅妆;
- 函數(shù)fetchUserOrder3檢測到異步執(zhí)行,直接返回一個future障癌,退出同步執(zhí)行凌外,將異步部分加入異步隊列中等待執(zhí)行;
- 函數(shù)createOrderMessage與6相同涛浙;
- 同步函數(shù)中異步函數(shù)createOrderMessage的同步部分執(zhí)行完畢康辑,繼續(xù)執(zhí)行下面同步代碼,打咏瘟痢:message bottom疮薇;
- 隊列要求先進先出,當最先加入異步隊列的任務已經(jīng)完成后我注,會以加入的次序執(zhí)行后續(xù)加入的異步代碼惦辛,后面的打印書序為:
order return
message return
Your order is: Large Latte
3.異步錯誤error
處理:
??在異步函數(shù)中,如同同步函數(shù)一樣使用try-catch
處理出現(xiàn)的錯誤或者異常仓手。
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 {
// 會拋出Cannot locate user order異常
await printOrderMessage();
}
4.處理流Streams
:
官方文檔
??Stream 是一系列異步事件的序列。其類似于一個異步的 Iterable玻淑,不同的是當你向 Iterable 獲取下一個事件時它會立即給你嗽冒,但是 Stream 則不會立即給你而是在它準備好時告訴你。
??Stream
概要:
-
Stream
提供一個異步的數(shù)據(jù)序列补履。 - 數(shù)據(jù)序列包括用戶生成的事件和從文件讀取的數(shù)據(jù)添坊。
- 你可以使用
Stream API
中的listen()
方法和await for
關鍵字來處理一個Stream
。 - 當出現(xiàn)錯誤時箫锤,
Stream
提供一種處理錯誤的方式贬蛙。 - Stream 有兩種類型:
Single-Subscription
和Broadcast
雨女。
4.1接收Stream
事件:
??Stream
可以通過許多方式創(chuàng)建,而這些所有的創(chuàng)建方式都可以相同的方式在代碼中使用:像使用for
循環(huán) 迭代一個Iterable
一樣阳准,我們可以使用 異步for
循環(huán) (通常我們直接稱之為await for
)來迭代Stream
中的事件氛堕。例如:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
??上面代碼只是簡單地接收整型事件流中的每一個事件并將它們相加,然后返回(被Future
包裹)相加后的整型值野蝇。當循環(huán)體結束時讼稚,函數(shù)會暫停直到下一個事件到達或Stream
完成。內部使用await for
循環(huán)的函數(shù)需要使用async
關鍵字標記绕沈。
下面的示例中使用了async*
函數(shù)生成一個簡單的整型Stream
來測試上一個代碼片段:
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;
}
}
// 調用
var stream = countStream(10);
var sum = sumStream(stream);
sum.then((value) {
print(value);
});
4.2Stream
錯誤處理:
??當Stream
再也沒有需要處理的事件時會變?yōu)橥瓿蔂顟B(tài)锐想。與此同時,調用者可以像接收到新事件回調那樣接收Stream
完成的事件回調乍狐。當使用await for
循環(huán)讀取事件時赠摇,循環(huán)會在Stream
完成時停止。
??有時在Stream
完成前會出現(xiàn)錯誤浅蚪;比如從遠程服務器獲取文件時出現(xiàn)網(wǎng)絡請求失敗藕帜,或者創(chuàng)建事件時出現(xiàn) bug;在出現(xiàn)錯誤時掘鄙,需要將錯誤告知使用者耘戚。
??Stream
可以像提供數(shù)據(jù)事件那樣提供錯誤事件。大多數(shù)Stream
會在第一次錯誤出現(xiàn)后停止操漠,但其也可以提供多次錯誤并可以在在出現(xiàn)錯誤后繼續(xù)提供數(shù)據(jù)事件收津。在本篇文檔中我們只討論Stream
最多出現(xiàn)并提供一次錯誤事件的情況。
??當使用await for
讀取Stream
時浊伙,如果出現(xiàn)錯誤撞秋,則由循環(huán)語句拋出,同時循環(huán)結束嚣鄙。你可以使用try-catch
語句捕獲錯誤吻贿。下面的示例會在循環(huán)迭代到參數(shù)值等于 4 時拋出一個錯誤:
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ù)計算機中,甚至在移動平臺上哑子,都在使用多核 CPU舅列。為了有效利用多核性能,開發(fā)者一般使用共享內存的方式讓線程并發(fā)地運行卧蜓。然而帐要,多線程共享數(shù)據(jù)通常會導致很多潛在的問題,并導致代碼運行出錯弥奸。
??為了解決多線程帶來的并發(fā)問題榨惠,Dart 使用isolate
替代線程,所有的 Dart 代碼均運行在一個 isolate 中。每一個isolate
有它自己的堆內存以確保其狀態(tài)不被其它 isolate 訪問赠橙。
??所有的 Dart 代碼都是在一個isolate
中運行耽装,而非線程。每個 isolate 都有一個單獨的執(zhí)行線程期揪,并且不與其他的isolate
共享任何可變對象掉奄。
你可以查閱下面的文檔獲取更多相關信息:
- Dart 中的并發(fā)特性
- dart:isolate API 參考 介紹了 Isolate.spawn() 和 TransferableTypedData 的用法
- Flutter 文檔上關于 后臺解析 的實用教程
- Isolate sample app