[Must Have 코드팩토리의 플러터 프로그래밍]

03 다트 비동기 프로그래밍

avocado8 2024. 4. 26. 21:26

 

 

3.1 동기 vs. 비동기 프로그래밍

동기 : 요청 후 응답을 받을 때까지 대기. 응답을 받은 후 다음 코드를 실행

비동기 : 응답을 대기하지 않으며 응답 순서 또한 요청 순서와 다를 수 있음

 

3.2 Future

Future 클래스 : 미래에 받아올 값을 뜻함. 제네릭으로 어떤 미래의 값을 받아올지를 정할 수 있다

ex) Future<String> name; //미래에 받을 String값

ex) Future<int> number; //미래에 받을 int값

비동기 프로그래밍에서는 요청 처리가 오래 걸리는 작업을 기다린 후 값을 받아와야 하기 때문에 미래값을 표현하는 Future 클래스를 사용.

void main() {
  addNumbers(1,1);
}

void addNumbers(int num1, int num2){
  print('계산 시작');
  //Future.delayed(): 일정 시간 후에 콜백 함수 실행
  Future.delayed(Duration(seconds: 3), (){
    print('$num1 + $num2 = ${num1+num2}');
  });
  print('계산 끝');
}

설정한 Duration동안 대기하라는 메시지를 받으면 cpu는 리소스를 허비하지 않고 다음 코드를 바로 실행하므로, 계산 시작 -> 계산 끝 -> 1+1=3 의 순서로 값이 출력된다.

 

3.3 async와 await

비동기 프로그래밍을 유지하면서도 코드 가독성을 유지하는 방법

void main() {
  addNumbers(1,1);
}

Future<void> addNumbers(int num1, int num2) async {
  print('계산 시작');
  await Future.delayed(Duration(seconds: 3), (){
    print('$num1 + $num2 = ${num1+num2}');
  });
  print('계산 끝');
}

async는 함수 매개변수 정의와 바디 사이에 입력

await는 대기하고 싶은 비동기함수 앞에 입력

이때는 Future.delayed()의 실행을 기다렸다가 아래 코드를 실행하게 되므로 계산 시작->1+1=2->계산 끝의 순서로 출력됨

 

3.3.1 결괏값 반환받기

void main() async {
  final result = await addNumbers(1,1);
  print('결과 $result');
  final result2 = await addNumbers(2,2);
  print('결과 $result2');
}

Future<int> addNumbers(int num1, int num2) async {
  print('계산 시작');
  await Future.delayed(Duration(seconds: 3), (){
    print('$num1 + $num2 = ${num1+num2}');
  });
  print('계산 끝');
  
  return num1+num2;
}

async / await 키워드를 사용한 함수에서도 결괏값을 반환받을 수 있다.

 

3.4 Stream

지속적으로 값을 반환받을 때 사용. Stream은 한 번 리슨하면 Stream에 주입되는 모든 값들을 지속적으로 받아온다. (Future은 반환값을 딱 한 번 받아내는 비동기 프로그래밍에 사용)

 

3.4.1 스트림 기본 사용법

스트림을 사용하려면 dart:async 패키지를 import해야 한다.

그다음 StreamController를 listen() 하면 값을 지속적으로 반환받을 수 있다.

import 'dart:async';

void main() {
  final controller = 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);
}

 

3.4.2 브로드캐스트 스트림

스트림은 단 한 번만(stream)을 사용할 수 있다.

하나의 스트림을 생성하고 여러 번 listen()함수를 실행하려면 브로드캐스트 스트림을 사용한다.

import 'dart:async';

void main() {
  final controller = StreamController();
  
  //여러 번 리슨할 수 있는 브로드캐스트스트림 객체 생성
  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);
}

 

3.4.3 함수로 스트림 반환하기

StreamController를 직접 사용하지 않고도 직접 스트림을 반환하는 함수를 작성할 수 있다.

Future를 반환하는 함수는 async로 함수를 선언하고 리턴으로 값을 반환

스트림을 반환하는 함수는 async*로 함수를 선언하고 yield 키워드로 값을 반환

import 'dart:async';

Stream<String> calculate(int num) async* {
  for(int i=0;i<5;i++){
    yield 'i = $i'; //값 반환
    await Future.delayed(Duration(seconds: 1));
  }
}

void playStream(){
  //listen()으로 콜백함수 입력
  calculate(1).listen((val){
    print(val);
  });
}

void main() {
  playStream();
}

i = 0부터 1초 간격으로 i =4까지 순차적으로 출력된다.