목차
0. 개요
- 네트워크 요청, 파일 읽기, 데이터베이스 쿼리 같은 작업은 비동기적으로 실행하는 것이 더 효율적
- 시간이 걸리는 작업을 기다리는 동안 다른 작업을 수행할 수 있음
- Future, async, await을 사용하여 비동기 코드를 작성
- stream 으로 연속된 값을 가져올 수 있음
- completer 를 이용해 Future를 직접 제어
1. Future - 비동기 작업
Dart에서 비동기 작업을 처리할 때 Future 객체를 사용한다
먼저, 일반적인 동기 함수 코드를 보고 비교해 보자
void longTask() {
print("작업 시작...");
sleep(Duration(seconds: 3)); // 3초 동안 멈춤
print("작업 완료!");
}
void main() {
print("A");
longTask();
print("B");
}
실행 결과
A
작업 시작...
(3초 후)
작업 완료!
B
비동기 함수 예시
- Future<자료형> 함수이름() async {코드}
- await를 사용하면 비동기 작업이 끝날 때까지 기다린 후 실행됨
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return "데이터 로드 완료";
}
void main() async {
print("시작");
String data = await fetchData();
print(data); // 2초 후 실행됨
print("끝");
}
실행 결과
A
B
작업 시작...
(3초 후)
작업 완료!
예외처리 적용
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
throw Exception("데이터 로드 실패!");
}
void main() async {
try {
String data = await fetchData();
print("데이터: $data");
} catch (e) {
print("에러 발생: $e");
}
}
2. Stream
Dart의 Future는 한 번만 값을 반환하지만,
Stream은 여러 개의 데이터를 지속적으로 반환할 때 사용
2.1. 기본 사용법
함수 선언
- Stream<자료형> 함수이름() async* {코드내용}
- await 을 이용해 기다림
- return 대신 yield 사용
비동기 함수 사용
- stream 객체 생성
- await for (자료형 변수 in stream객체) {반복코드}
Stream<int> numberStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
Stream<int> stream = numberStream();
await for (int num in stream) {
print("받은 데이터: $num");
}
print("스트림 종료");
}
2.2. Stream을 listen()으로 사용
listen(함수)을 사용하면 데이터를 계속 수신, () 안에 실행할 함수를 넣어줄 수 있음
data는 Stream에서 yield로 반환된 값이 자동으로 전달
onDone을 사용하면 스트림 종료 시 실행할 코드 작성 가능
void main() {
stream.listen(
(int data) => print("받은 데이터: $data"),
onDone: () => print("스트림 완료"),
);
}
where() 를 이용한 필터링
void main() {
Stream<int> stream = Stream.fromIterable([1, 2, 3, 4, 5, 6, 7]);
stream.where((int num) => num % 2 == 0).listen(
(int data) => print("짝수 데이터: $data"),
);
}
2.3. 첫번째 값만 가져오기
stream.first 를 이용해 첫번째 값만 가져올 수 있음
void main() async {
Stream<int> stream = Stream.fromIterable([10, 20, 30]);
int firstValue = await stream.first;
print("첫 번째 값: $firstValue");
}
2.4. 예외처리
onError : 함수 를 사용해 예외처리
error 변수에는 예외(Exception) 객체가 들어옴
Stream<int> errorStream() async* {
yield 1;
yield 2;
throw Exception("에러 발생!");
yield 3;
}
void main() {
errorStream().listen(
(data) => print("받은 데이터: $data"),
onError: (error) => print("오류 발생: $error"),
onDone: () => print("스트림 종료"),
);
}
3. Completer
3.1. 설명
함수가 아래 코드를 return 해주는 함수를 만든다고 하면
return completer.future;
아래 코드가 실행된 상태가 될때까지 기다린다.
completer.complete()
예를 들어 버튼을 클릭할 때 까지 기다린다고 할때
유용하게 사용할 수 있겠다.
3.2. Completer 를 사용하는 함수 정의
진행 순서
1. Future를 반환하는 함수를 async로 선언
2. Completer를 선언한다.
Completer<자료형> completer = Completer<자료형>();
2. completer 완료
completer.complete()
3. 예외 발생
completer.completeError()
4. Future 반환
return completer.future
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<Map<String, dynamic>> fetchUserData() async {
Completer<Map<String, dynamic>> completer = Completer<Map<String, dynamic>>();
try {
print("서버에서 사용자 데이터 가져오는 중...");
// 실제 API 요청 (GET 요청)
var response = await http.get(Uri.parse("https://jsonplaceholder.typicode.com/users/1"));
if (response.statusCode == 200) {
Map<String, dynamic> userData = jsonDecode(response.body);
completer.complete(userData); // Future 완료 (성공)
} else {
completer.completeError("서버 오류: ${response.statusCode}");
}
} catch (error) {
completer.completeError("네트워크 오류 발생: $error"); // Future 실패 (네트워크 오류)
}
return completer.future;
}
함수 실행
await 로 실행하면 됨
void main() async {
print("사용자 데이터 요청 중...");
try {
Map<String, dynamic> user = await fetchUserData();
print("사용자 데이터: $user");
} catch (error) {
print("에러 발생: $error");
}
print("작업 완료!");
}
'flutter' 카테고리의 다른 글
[flutter] dart 언어 - 함수 (4) (0) | 2025.02.22 |
---|---|
[flutter] dart 언어 - 조건문 & 반복문 (3) (0) | 2025.02.22 |
[flutter] dart 언어 - print, 주석, 변수, 자료형 (2) (0) | 2025.02.22 |
[flutter] 환경 세팅 (1) (0) | 2025.02.22 |