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() vs await for 선택 가이드
- 파일 읽기, 유한한 시퀀스 데이터 →
await for - UI 이벤트, 지속 메시지 전달 등 무한 스트림 →
.listen() - await for가 시작되면 스트림이 끝날 때까지 대기하므로 무한 이벤트 스트림은 프로그램 정지 원인이 됩니다
dart:async 라이브러리
Previous: dart:io