dart:async 提供 Dart 非同步程式設計的核心功能:Future、Stream。
- 使用
Future / Stream不一定需要手動匯入 dart:async,dart:core 已經匯出這些類型;- 若要使用
StreamController、StreamSubscription、ParallelWaitError等型別,就必須明確匯入:
import 'dart:async';Code language: JavaScript (javascript)
兩大核心觀念:
- Future:代表單次非同步結果(在未來某個時間點只會產生一個值、或是一次錯誤)
- Stream:代表連續的一系列非同步資料(持續產生多筆數據,類似事件串流)
Future
1. async / await
優先建議使用,可讀性優於 .then() 鏈式呼叫
- 標記
async的函式會回傳 Future; await等待 Future 執行完畢,暫停目前程式直到取得結果;- 透過
try-catch攔截非同步例外。
// async 函式回傳 Future
Future<String> findEntryPoint() async => "main.dart";
Future<int> runExecutable(String entry, List args) async => 0;
Future<void> flushThenExit(int code) async {}
// await 寫法(推薦)
Future<void> runUsingAsyncAwait() async {
try {
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, []);
await flushThenExit(exitCode);
} catch (e) {
// 統一攔截例外
print("發生錯誤:$e");
}
}Code language: JavaScript (javascript)
2. .then() / .catchError() 傳統鏈式寫法
.then():Future 成功完成後執行回呼函式;會回傳新的 Future,可持續鏈式呼叫.catchError():捕捉例外;必須接在 then 的回傳值後面鏈接,否則無法捕獲 then 內部發生的錯誤
void runUsingFuture() {
findEntryPoint()
.then((entryPoint) {
return runExecutable(entryPoint, []);
})
.then(flushThenExit)
.catchError((e) {
print("例外:$e");
});
}Code language: JavaScript (javascript)
3. 等待多個並行 Future
① Future.wait()
等待所有任務執行完成;只要任一 Future 發生錯誤,整體操作會直接失敗
Future<void> task1() async {}
Future<void> task2() async {}
Future<void> task3() async {}
void testWait() async {
await Future.wait([
task1(),
task2(),
task3(),
]);
print("所有任務執行完畢");
}Code language: JavaScript (javascript)
② List.wait / Record.wait(新功能,精細處理並行錯誤)
會拋出 ParallelWaitError,能夠分辨哪些任務成功、哪些失敗,任務之間不會互相中斷。
Future<int> delete() async => 1;
Future<String> copy() async => "ok";
Future<bool> errorTask() async {
throw Exception("任務失敗");
}
void main() async {
try {
// 清單形式
var results = await [delete(), copy(), errorTask()].wait;
} on ParallelWaitError<List<Object?>, List<AsyncError?>> catch (e) {
print(e.values); // 成功任務帶有數值,失敗則為null
print(e.errors); // 成功任務為null,失敗存放錯誤資訊
}
// Record元組形式:支援不同型別的Future
try {
final (resInt, resStr, resBool) = await (delete(), copy(), errorTask()).wait;
} on ParallelWaitError<(int?, String?, bool?), (AsyncError?, AsyncError?, AsyncError?)> catch(e) {
}
}Code language: JavaScript (javascript)
Stream
Stream 代表一連串連續的非同步資料(檔案逐行讀取、按鈕點擊事件、網路資料串流等場景)。
兩種使用方式
1:await for 非同步for迴圈(語法簡潔,類似同步走訪)
適合有限數據串流;不適用無限持續事件串流(例如頁面點擊事件),會阻擋後續程式執行。
import 'dart:io';
Future<void> readFileByAwaitFor() async {
var file = File("config.txt");
Stream<List<int>> input = file.openRead();
var lines = input
.transform(utf8.decoder)
.transform(const LineSplitter());
try {
await for (final line in lines) {
print("讀取一行:$line");
}
print("檔案讀取完畢");
} catch (e) {
print("讀取發生錯誤 $e");
}
}Code language: PHP (php)
2:.listen() 訂閱監聽(適合無限事件串流)
inputStream.listen(
(data) {
// 收到資料時執行回呼
},
onError: (err) {
// 發生錯誤時觸發
},
onDone: () {
// 串流正常關閉完成
},
);Code language: JavaScript (javascript)
listen 會回傳
StreamSubscription,可以呼叫.cancel()取消訂閱。
常用 Stream 操作方法
- 篩選:
where() - 截取:
take()、takeWhile() - 跳過:
skip()、skipWhile() - 取得單一元素:
first、last、single、firstWhere()
Stream 資料轉換 transform()
透過轉換器更改串流的資料型別(典型場景:二進位串流轉文字字串)
var lines = inputStream
.transform(utf8.decoder) // List<int> → String
.transform(const LineSplitter()); // String → 逐行文字Code language: JavaScript (javascript)
listen() 與 await for 選擇建議
- 檔案讀取、有限序列資料 →
await for - UI事件、持續推送訊息等無限串流 →
.listen() - await for 啟動後會持續等待串流結束;無限事件串流將造成程式卡住
dart:async 程式庫
Previous: dart:io