[Flutter / Dart] 비동기 프로그래밍
동기 vs 비동기 프로그래밍
동기란?
- 한 작업이 끝나야 다음 작업을 실행함
- 작업들이 순차적으로 실행됨.
- `blocking`방식 -> 이전 작업이 끝날 때까지 기다림.
void main() {
print('A');
print('B');
print('C');
}
/* 출력값
A
B
C
*/
비동기란?
- 시간이 오래 걸릴 수 있는 작업을 미뤄두고, 다음 작업을 먼저 실행함
- 대표적인 비동기 작업 : API 호출, 파일 읽기, 타이머 등
- `Future`, `async`, `await` 키워드를 통해 구현
1. `Future`
- `Future`클래스는 '미래'라는 단어의 의미대로 미래에 받아올 값을 뜻함.
- `List`나 `Set`처럼 제네릭으로 어떤 미래의 값을 받아올지를 정할 수 있음
Future<String> name; // 미래에 받을 String값
Future<int> number; // 미래에 받을 int값
Future<bool> inOpened; // 미래에 받을 boolen값
비동기 프로그래밍은 서버 요청과 같이 오래 걸리는 작업을 기다린 후 값을 받아와야 하기 때문에 미래의 값을 표현하는 `Future`클래스가 필요함!
특정 기간 동안 아무것도 하지 않고 기다리는 `Future.delayed()`를 사용해보면서 코드를 작성해 보겠음.
// 비동기
// Future
void main() {
addNumbers(1, 1);
}
void addNumbers(int number1, int number2) {
print("$number1 + $number2 계산 시작!");
// Future.delayed()를 사용하면 일정 시간 후에 콜백 함수를 실행할 수 있음
Future.delayed(Duration(seconds: 3), (){
print("$number1 + $number2 = ${number1 + number2}");
});
print("$number1 + $number2 코드 실행 끝");
}
/* 출력값
1 + 1 계산시작!
1 + 1 코드 실행 끝
1 + 1 = 2
*/
첫 번째 파라미터에 대기할 시간 `Duration(seconds: 3)`을 입력하고 두 번째 파라미터에 대기후 실행할 콜백 함수를 입력하면 됨.
그러면 `addNumbers()`함수는 `print()`함수를 실행하고 `Future.delayed()`를 통해 3초간 대기함. 그다음 마지막 `print()`함수를 실행하게됨.
이렇게 되면 마지막 `print()`는 `Future.delayed()`안에 있는 `print()`후에 호출되는거 아닌가? 싶을 수 있지만 아님!
`Future.delayed()`는 비동기 연산이기 때문에 CPU가 3초간 대기해야 한다는 메세지를 받으면 리소스를 허비하지 않고 다음 코드를 바로 실행함.
2. `async`와 `await`
`async`와 `await`키워드를 사용하면 비동기 프로그래밍을 유지하면서 코드 가독성을 유지할 수 있음.
2-1 `async`
- 함수 선언할 때 붙임
- 이 함수 안에는 비동기 코드가 들어올 수 있다고 Dart에게 알려주는 것
- 반환값은 보통 `Future<T>`형식이 됨
// async와 await
void main() {
addNumbers(1, 1);
}
// async키워드는 함수 파라미터 정의와 바디 사이에 입력
Future<void> addNumbers(int number1, int number2) async {
print("$number1 + $number2 계산 시작!");
await Future.delayed(Duration(seconds: 3), (){
print("$number1 + $number2 = ${number1 + number2}");
});
print("$number1 + $number2 코드 실행 끝");
}
/* 출력값
1 + 1 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝
*/
위 코드와 같이 함수를 `async` 로 지정해주고 나서 대기하고 싶은 비동기 함수를 실행할 때 `await`키워드를 사용하면 코드는 작성한 순서대로 실행됨.
이렇게 되면 비동기 프로그래밍이아니라 동기 프로그래밍처럼 되는거 아니냐? 할 수 있는데 `async`와 `await`키워드를 사용하면 비동기 프로그래밍 특징을 그대로 유지하며 프로그램이 실행됨.
그러면 `void main()`에 `addNumbers(2, 2)`를 추가하면 어떻게 될까?
void main() {
addNumbers(1, 1);
addNumbers(2, 2);
}
/* 출력값
1 + 1 계산 시작!
2 + 2 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝
2 + 2 = 4
2 + 2 코드 실행 끝
*/
왜 이런 결과가 나왔나?
`addNumbers()`함수가 두번 실행됐음. 그런데, `addNumbers(1, 1)`가 끝나기 전에 `addNumbers(2, 2)`가 실행됨. 그 이유는
`addNumbers()`가 비동기로 실행됐기 때문임. `addNumbers(1, 1)`의 `Future.delayed()`가 실행되며 3초를 기다려야 할 때 CPU의 리소스가 낭비되지 않고 바로 다음 실행할 코드인 `addNumbers(2, 2)`가 실행 된 것임.
그런데 만약 `addNumbers(1, 1)`과 `addNumbers(2, 2)`가 순차적으로 실행되길 원한다면 `async`와 `await`를 추가해주면 됨
// addNumbers가 둘 다 순차적으로 실행되는 방법
void main() async {
await addNumbers(1, 1);
await addNumbers(2, 2);
}
Future<void> addNumbers(int number1, int number2) async {
print("$number1 + $number2 계산 시작!");
await Future.delayed(Duration(seconds: 3), () {
print("$number1 + $number2 = ${number1 + number2}");
});
print("$number1 + $number2 코드 실행 끝");
}
/* 출력값
1 + 1 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝
2 + 2 계산 시작!
2 + 2 = 4
2 + 2 코드 실행 끝
*/
2-2 결과값 반환받기
`async`와 `await`키워드를 사용한 함수에서도 결과값을 받을 수 있음. 이때 `Future`클래스를 사용함.
입력된 두 숫자를 더한 결과값을 반환하는 `addNumbers()`함수의 코드를 수정해보겠음.
// async와 await 키워드를 사용한 함수에서 결과값 받기
void main() async {
final result = await addNumbers(1, 1);
print("결과값 $result");
final result2 = await addNumbers(2, 2);
print("결과값 $result2");
}
Future<int> addNumbers(int number1, int number2) async {
print("number1 + $number2 계산 시작!");
await Future.delayed(Duration(seconds: 3), (){
print("$number1 + $number2 = ${number1 + number2}");
});
print("$number1 + $number2 코드 실행 끝");
return number1 + number2;
}
/* 출력값
number1 + 1 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝
결과값 2
number1 + 2 계산 시작!
2 + 2 = 4
2 + 2 코드 실행 끝
결과값 4
*/
3. Stream
- `Future`는 한 번만 값을 비동기로 전달
- `Stream`은 여러 개의 값을 시간 간격을 두고 비동기로 전달
- 예를 들어, 센서 값, 사용자 입력, 채팅 메세지 등 계속 들어오는 데이터를 다룰 때 사용
3-1 `Stream` 기본 사용법
- `Stream`을 사용하려면 플러터에서 기본으로 제공하는 `dart:async`패키지를 불러와야함.
- `dart:async`패키지에서 제공하는 `StreamController`를 `listen()`해야 값을 지속적으로 반환 받을 수 있음
// Stream 기본 사용법
import "dart:async";
void main() {
final controller = StreamController(); // StreamController 선언
final stream = controller.stream; // 스트림 가져오기
// Stream에 listen() 함수를 실행하면 값이 주입될 때마다 콜백 함수를 실행할 수 있음
final streamListener1 = stream.listen((val) {
print(val);
});
// Stream에 값을 주입하기
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
controller.sink.add(4);
}
/* 출력값
1
2
3
4
*/
3-2 브로드캐스트 스트림
스트림은 단 한번만 `listen()`을 실행할 수 있음.
하지만 하나의 스트림을 생성하고 여러 번 `listen()`함수를 실행할 떄가 있음. 이 때 브로드 캐스트 스트림을 사용하면 스트림을 여러 번 `listen()`하도록 변환할 수 있음.
import "dart:async";
void main() {
final controller = StreamController();
// 여러 번 리슨할 수 있는 Broadcast Stream 객체 생성
final stream = controller.stream.asBroadcastStream();
// 첫 번째 listen() 함수
final streamListener1 = stream.listen((val) {
print("listening 1");
print(val);
});
// 두 번째 listen() 함수
final streamListener2 = stream.listen((val) {
print("listening 2");
print(val);
});
// add()를 실행할 때마다 listen()하는 모든 콜백 함수에 값이 주입됨
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
}
/* 출석값
listening 1
1
listening 2
1
listening 1
2
listening 2
2
listening 1
3
listening 2
3
*/
3-3 함수로 스트림 반환하기
`StreamController`를 직접 사용하지 않고도 직접 스트림을 반환하는 함수를 작성할 수도 있음.
`Future`를 반환하는 함수를 `async`로 함수를 선언하고 `return`키워드로 값을 반환하면 됨.
스트림을 반환하는 함수는 `async*`로 함수를 선언하고 `yield`키워드로 값을 반환해주면 됨.
// 함수로 스트림 반환하는 방법
import "dart:async";
// Stream을 반환하는 함수는 async로 선언함
Stream<String> calculate(int number) async* {
for (int i = 0; i < 5; i++) {
// StreamController의 add()처럼 yield 키워드를 이용해서 값 변환
yield 'i = $i';
await Future.delayed(Duration(seconds: 1));
}
}
void playStream() {
calculate(1).listen((val){
print(val);
});
}
void main() {
playStream();
}
/* 출력값
i = 0
i = 1
i = 2
i = 3
i = 4
*/
https://github.com/Leedoseo/Flutter_Async
GitHub - Leedoseo/Flutter_Async
Contribute to Leedoseo/Flutter_Async development by creating an account on GitHub.
github.com
https://www.notion.so/Dart-Flutter-Study-1cf9fd9f157980a5ab7efad394810871?pvs=4
Dart / Flutter Study | Notion
Made with Notion, the all-in-one connected workspace with publishing capabilities.
www.notion.so
출처 : 코드팩토리의 플러터 프로그래밍
'Dart 언어' 카테고리의 다른 글
[Flutter / Dart] 다트 3.0 신규 문법 (구조 분해) (0) | 2025.04.11 |
---|---|
[Flutter / Dart] 다트 3.0 신규 문법 (레코드) (0) | 2025.04.11 |
[Flutter / Dart] 객체지향 프로그래밍(스태틱과 캐스케이드 연산자) (1) | 2025.04.08 |
[Flutter / Dart] 객체지향 프로그래밍(추상과 제네릭) (0) | 2025.04.08 |
[Flutter / Dart] 객체지향 프로그래밍(인터페이스와 믹스인) (1) | 2025.04.07 |