본문 바로가기
DEV/dart & flutter

다트 비동기 프로그래밍 Future, async/await, Stream

by 올커 2024. 1. 1.

 

 

 

Dart는 동기, 비동기 프로그래밍을 지원한다.

 - 동기 : 요청하고 나서 응답이 올 때 까지 대기했다가 응답을 받으면 다음 코드 진행

 - 비동기 : 요청하고 응답을 받지 않아도 다음 코드 진행

예를 들어 DB에서 게시판의 글을 가져오는 작업의 경우 등 시간이 걸리는 작업은 동기로 실행하면 앱이 매우 느려지기 때문에 이런 경우 비동기로 처리해야 한다.


🎆 다트 비동기 프로그래밍

1. Future

 - Future 클래스는 미래에 받아올 값을 말하며, List나 Set처럼 제네릭으로 받아올 값을 정할 수 있다.

void main(){
  Future<String> name;    // 받아올 String값
  Future<int> number;     // 받아올 int값
  Future<bool> isOpened;  // 받아올 boolean값
}

 

 - Future.delayed는 특정 기간동안 아무 것도 하지 않고 대기하였다가, 일정 시간 후 콜백 함수를 실행한다.

 - 아래 출력 순서를 보면 '1 + 1 코드 실행 끝' 문구가 먼저 출력된 후 future.delayed 대기가 끝나고 계산결과가 마지막으로 출력되는 모습을 확인할 수 있다.

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

void addNumbers(int number1, int number2){
  print('$number1 + $number2 계산 시작!');

  Future.delayed(Duration(seconds: 3), (){ 		 // Future.delayed()를 사용하면 일정 시간 후에 콜백 함수를 실행할 수 있음
    print('$number1 + $number2 = ${number1 + number2}');
  });

  print('$number1 + $number2 코드 실행 끝');
}

// 실행결과
// 1 + 1 계산 시작!
// 1 + 1 코드 실행 끝
// 1 + 1 = 2

2. async와 await

 - Future에서는 코드가 작성된 대로 실행되지 않아 혼동될 수 있는 문제가 있다. async와 await는 이를 방지하고 코드 가독성을 유지시킬 수 있는 장점이 있다.

 - 아래 코드를 보면 addNumbers(1, 1)이 끝나기 전에 대기 없이 addNumbers(2,2)가 실행되었다. 이는 addNumbers() 함수가 비동기 프로그래밍으로 실행되었기 때문이다.

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

void addNumbers(int number1, int number2) async {		// async 키워드는 함수 매개변수 정의와 바디 사이에 입력
  print('$number1 + $number2 계산 시작!');

  await Future.delayed(Duration(seconds: 3), (){		  // await는 대기하고 싶은 비동기 함수 앞에 입력
    print('$number1 + $number2 = ${number1 + number2}');
  });

  print('$number1 + $number2 코드 실행 끝');
}

// 실행결과
// 1 + 1 계산 시작!
// 2 + 2 계산 시작!
// 1 + 1 = 2
// 1 + 1 코드 실행 끝
// 2 + 2 = 4
// 2 + 2 코드 실행 끝

 

 - 만약 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 코드 실행 끝

 

 - 일반 함수와 같이 결과값을 반환받으려면 아래와 같이 변수에 할당하여 사용할 수 있다.

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;
}


// 실행결과
// 1 + 1 계산 시작!
// 1 + 1 = 2
// 1 + 1 코드 실행 끝
// 결괏값 2
// 2 + 2 계산 시작!
// 2 + 2 = 4
// 2 + 2 코드 실행 끝
// 결괏값 4

3. Stream

 - 반환값을 지속적으로 받아오고 싶을 경우 Stream을 사용한다.(Future는 반환값을 딱 한번 반환)

 - Stream을 사용하려면 dart:async 패키지를 로드해야 한다.  → import 'dart:async'

 - controller = StreamController(); 와 같이 stream controller를 선언하여 이를 통해 stream을 로드하여 사용한다.

 - Stream.listen() : 값이 주입될 때마다 콜백 함수 실행

 - sink.add() : stream에 값을 주입

import 'dart:async';		// 패키지 로드

void main() {
  final controller = StreamController();  // StreamController 선언
  final stream = controller.stream;  // Stream 로드

  final streamListener1 = stream.listen((val) { 		 // Stream에 listen() 함수 실행 : 값이 주입될 때마다 콜백 함수를 실행 가능
    print(val);
  });

  // sink.add() : Stream에 값을 입력
  controller.sink.add(1);
  controller.sink.add(2);
  controller.sink.add(3);
  controller.sink.add(4);
}

// 실행결과
// 1
// 2
// 3
// 4

 

 - 브로드캐스트 스트림 : 하나의 스트림을 생성하고 여러 번 listen()함수를 실행하고 싶을 때

 - controller.stream.asBroadcastStream(); 으로 BroadcastStream 객체 생성

import 'dart:async';

void main() {
  final controller = StreamController();

  final stream = controller.stream.asBroadcastStream(); 		 // 여러 번 리슨할 수 있는 Broadcaste Stream 객체 생성

  final streamListener1 = stream.listen((val) {		  // 첫 번째 listen() 함수
    print('listening 1');
    print(val);
  });

  final streamListener2 = stream.listen((val) {		  // 두 번째 listen() 함수
    print('listening 2');
    print(val);
  });

  controller.sink.add(1);		  // add()를 실행할 때마다 listen()하는 모든 콜백 함수에 값이 주입
  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

 

 - 함수로 Stream 반환 : 이는 async*로 함수를 선언하고 yield 키워드로 값을 반환(Stream controller를 사용하지 않음)

import 'dart:async';

Stream<String> calculate(int number) async* {		// Stream을 반환하는 함수를 async*로 선언
  for (int i = 0; i < 5; i++) {
    yield 'i = $i';    // yield 키워드를 이용해서 값 반환
    await Future.delayed(Duration(seconds: 1));
  }
}

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

void main() {
  playStream();
}

// 실행결과
// i = 0
// i = 1
// i = 2
// i = 3
// i = 4
반응형

'DEV > dart & flutter' 카테고리의 다른 글

Dart 객체지향 프로그래밍, 클래스  (1) 2023.12.31
Dart 기본 문법 정리  (0) 2023.12.27

댓글