Skip to content

Commit

Permalink
Create ch03.md (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
deepbig authored Dec 16, 2024
1 parent 6c50f90 commit 6a25021
Showing 1 changed file with 246 additions and 0 deletions.
246 changes: 246 additions & 0 deletions hong/flutter/코드팩도리의 플러터 프로그래밍/ch03.md
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을 반환할 수 있음.

0 comments on commit 6a25021

Please sign in to comment.