-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
### 03. 다트 비동기 프로그래밍 | ||
|
||
### 학습목표 | ||
|
||
- 다트 언어는 동기/비동기 프로그래밍을 지원. | ||
- 다트를 사용해 동기/비동기 프로그래밍을 하는 방법 공부. | ||
|
||
### 3.1. 동기 vs 비동기 프로그래밍 | ||
|
||
<img width="648" alt="Screenshot 2024-07-27 at 10 47 59 AM" src="https://github.com/user-attachments/assets/8fa41de6-a826-48db-ac89-5eb401f8f101"> | ||
|
||
- 동기: 함수를 실행하면 다음 코드가 실행되기 전에 해당 함수의 결과값이 먼저 반환됨. | ||
- 비동기: 요청한 결과를 기다리지 않으며 응답 순서 또한 요청한 순서와 다를 수 있음. | ||
- 컴퓨터 자원을 효율적으로 사용: 예) 데이터베이스 조회, 복잡한 계산 작업 등 시간이 걸리는 작업. | ||
|
||
### 3.2. Future | ||
|
||
- Future class: 미래에 받아올 값을 뜻함. List나 Set 처럼 제네릭으로 어떤 미래의 값을 받아올지를 정할 수 있음. | ||
|
||
```dart | ||
Future<String> name; // 미래에 받을 String 값 | ||
Future<int> number; // 미래에 받을 int 값 | ||
Future<bool> isOpened; // 미래에 받을 boolean 값 | ||
``` | ||
|
||
- 비동기 프로그래밍은 서버 요청과 같이 오래 걸리는 작업을 기다린 후 값을 받아와야 하기 때문에 미래값을 표현하는 Future 클래스가 필요. | ||
|
||
```dart | ||
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 | ||
``` | ||
|
||
- Future.delayed의 첫 번째 매개변수 = 대기할 기간, 두 번째 매개변수 = 대기 후 실행할 콜백 함수 | ||
|
||
### 3.3. async & await | ||
|
||
- Future을 개발자가 코드를 작성한 순서대로 실행되기 위해 필요. | ||
- async와 await 키워드를 사용하면 비동기 프로그래밍을 유지하면서도 코드 가독성을 유지할 수 있음 | ||
|
||
```dart | ||
void main() { | ||
addNumbers(1, 1); | ||
addNumbers(2, 2); | ||
} | ||
Future<void> addNumbers(int number1, int number2) async { | ||
print('$number1 + $number2 계산 시작'); | ||
// await는 대기하고 싶은 비동기 함수 앞에 입력. | ||
await Future.delayed(Duration(seconds: 3), () { | ||
print('$number1 + $number2 = ${number1 + number2}'); | ||
}); | ||
print('$number1 + $number2 계산 종료'); | ||
} | ||
// 결과 | ||
// 1 + 1 계산 시작 | ||
// 2 + 2 계산 시작 | ||
// 1 + 1 = 2 | ||
// 1 + 1 계산 종료 | ||
// 2 + 2 = 4 | ||
// 2 + 2 계산 종료 | ||
``` | ||
|
||
- 함수를 async로 지정하고, 대기하고 싶은 비동기 함수를 실행할 때 await 키워드를 사용. | ||
- async와 await 키워드를 사용하면 비동기 프로그래밍 특징을 그대로 유지하며 코드가 작성된 순서대로 프로그램을 실행함. | ||
|
||
**3.3.1. 결괏값 반환받기** | ||
|
||
```dart | ||
void main() async { | ||
final result = await addNumbers(1, 1); | ||
print('결괏값 $result'); // 일반 함수와 동일하게 반환 값을 받음. | ||
} | ||
Future<int> addNumbers(int number1, int number2) async { | ||
print('$number1 + $ number2 계산 시작'); | ||
await Future.delayed(Duration(seconds: 3), (){ | ||
print('$number1 + $number2 = ${number1 + number2}'); | ||
} | ||
return number1 + number2; | ||
} | ||
// 1 + 1 계산 시작 | ||
// 1 + 1 = 2 | ||
// 결괏값 2 | ||
``` | ||
|
||
### 3.4. Stream | ||
|
||
- Future는 반환값을 딱 한 번 받아내는 비동기 프로그램에 사용. | ||
- 지속적으로 값을 반환받을 때는 Stream을 사용. | ||
- Stream은 한 번 listen 하면 Stream에 주입되는 모든 값들을 지속적으로 받아옴. | ||
|
||
Key Differences | ||
|
||
- Nature of Asynchronous Operations: | ||
- Future: Handles a single asynchronous operation. | ||
- Stream: Handles a series of asynchronous operations/events over time. | ||
- Number of Values: | ||
- Future: Completes with a. ingle value or an error. | ||
- Stream: Can emit multiple values or errors over time. | ||
- Listeners: | ||
- Future: Can have only one listener. | ||
- Stream: Can have multiple listeners in case of broadcast streams, but single-subscription streams can have only one listener. | ||
- Use Cases: | ||
- Future: Ideal for operations like fetching data, reading files, or any one-time task. | ||
- Stream: Ideal for handling continuous data like UI events, real-time data feeds, or any scenario involving multiple asynchronous events. | ||
|
||
|
||
<img width="589" alt="Screenshot 2024-07-28 at 12 19 25 AM" src="https://github.com/user-attachments/assets/f46cf886-930b-4fed-9126-66b780b9bab1"> | ||
|
||
**3.4.1. 스트림 기본 사용법** | ||
|
||
- stream을 사용하려면 플러터에서 기본으로 제공하는 dart:async 패키지를 불러와야 함. | ||
- 그다음 dart:async 패키지에서 제공하는 StreamController를 listen()해야 값을 지속적으로 반환받을 수 있습니다. | ||
|
||
```dart | ||
import 'dart:async'; | ||
void main() { | ||
final controller = StreamController(); // StreamController 선언 | ||
final stream = controller.stream; // Stream 가져오기 | ||
// Stream에 listen() 함수를 실행하면 값이 주입될 때마다 콜백 함수를 실행할 수 있음. | ||
stream.listen((val) { | ||
print(val); | ||
}); | ||
controller.sink.add(1); | ||
controller.sink.add(2); | ||
controller.sink.add(3); | ||
controller.sink.add(4); | ||
} | ||
``` | ||
|
||
**3.4.2. 브로드캐스트 스트림** | ||
|
||
- 스트림은 단 한 번만 listen()을 실행할 수 있음. | ||
- 하지만 때때로 하나의 스트림을 생성하고 여러 번 listen() 함수를 실행하고 싶을 때가 있음. | ||
- 이럴 때 broadcast stream을 사용하면 stream을 여러 번 listen() 하도록 변환할 수 있음. | ||
|
||
```dart | ||
import 'dart:async'; | ||
void main() { | ||
final controller = StreamController(); | ||
// 여러 번 리슨할 수 있는 Broadcast Stream 객체 생성 | ||
final stream = controller.stream.asBroadcastStream(); | ||
// 첫 번째 listen() 함수 | ||
final streamListener1 = stream.listen((val) { | ||
print('listening 1: $val'); | ||
}); | ||
// 두 번째 listen() 함수 | ||
final streamListener2 = stream.listen((val) { | ||
print('listening 2: $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.4.3. 함수로 스트림 반환하기** | ||
|
||
- StreamController를 직접 사용하지 않고도 직접 스트림을 반환하는 함수를 작성할 수도 있음. | ||
- Future를 반환하는 함수는 `async`로 함수를 선언하고 return 키워드로 값을 반환하면 됨. | ||
- Stream을 반환하는 함수는 `async*`로 함수를 선언하고 yield 키워드로 값을 반환해주면 됨. | ||
|
||
```dart | ||
import 'dart: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: number)); | ||
} | ||
} | ||
void playStream() { | ||
// StreamController와 마찬가지로 listen() 함수로 콜백 함수 입력 | ||
calculate(1).listen((val) { | ||
print(val); | ||
}); | ||
} | ||
void main() { | ||
print('시작'); | ||
playStream(); | ||
print('종료'); | ||
} | ||
// 결괏값 | ||
// 시작 | ||
// 종료 | ||
// i = 0 | ||
// i = 1 | ||
// i = 2 | ||
// i = 3 | ||
// i = 4 | ||
``` | ||
|
||
### 핵심요약 | ||
|
||
1. `비동기 프로그래밍`을 이용하면 오랜 기간 CPU의 리소스가 막히는 상황을 방지할 수 있음. | ||
2. `async` 키워드를 사용하면 비동기 함수를 정의할 수 있음. | ||
3. `await` 키워드를 사용하면 비동기 함수를 논리적 순서대로 실행할 수 있음. | ||
4. `Future`는 비동기 응답을 한 번만 받을 때 사용하는 클래스. | ||
5. `Stream`은 지속적으로 listen하여 비동기 응답을 받을 때 사용하는 클래스. | ||
1. 한 번 listen()하면 지속적으로 값을 받아올 수 있음. | ||
2. `async*` 키워드로 정의. | ||
3. 값을 반환할 때는 `yield` 키워드를 사용. | ||
4. 함수에서 Stream을 반환할 수 있음. |