diff --git "a/hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch03.md" "b/hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch03.md" new file mode 100644 index 0000000..c3beafb --- /dev/null +++ "b/hong/flutter/\354\275\224\353\223\234\355\214\251\353\217\204\353\246\254\354\235\230 \355\224\214\353\237\254\355\204\260 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/ch03.md" @@ -0,0 +1,246 @@ +### 03. 다트 비동기 프로그래밍 + +### 학습목표 + +- 다트 언어는 동기/비동기 프로그래밍을 지원. +- 다트를 사용해 동기/비동기 프로그래밍을 하는 방법 공부. + +### 3.1. 동기 vs 비동기 프로그래밍 + +Screenshot 2024-07-27 at 10 47 59 AM + +- 동기: 함수를 실행하면 다음 코드가 실행되기 전에 해당 함수의 결과값이 먼저 반환됨. +- 비동기: 요청한 결과를 기다리지 않으며 응답 순서 또한 요청한 순서와 다를 수 있음. + - 컴퓨터 자원을 효율적으로 사용: 예) 데이터베이스 조회, 복잡한 계산 작업 등 시간이 걸리는 작업. + +### 3.2. Future + +- Future class: 미래에 받아올 값을 뜻함. List나 Set 처럼 제네릭으로 어떤 미래의 값을 받아올지를 정할 수 있음. + +```dart +Future name; // 미래에 받을 String 값 +Future number; // 미래에 받을 int 값 +Future 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 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 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. + + +Screenshot 2024-07-28 at 12 19 25 AM + +**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 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을 반환할 수 있음.