14. 플러터 Future, async, await

Zero·2022년 2월 12일
2

플러터

목록 보기
14/21

1. 동기와 비동기

- 동기 : 모든 동작을 차례대로 완료 후 수행하는 것
- 비동기 : 어떤 동작이 완료가 되지 않아도 다음 동작을 수행하는 것


2. Future

  • 비동기 처리를 위해 존재
  • Dart의 Future는 지금은 없지만 미래에 요청한 데이터 혹은 에러가 담길 그릇

< 예시 >

Future<int> 라는 상자가 있다. 지금 당장은 닫혀 있고, 이 상자를 준 함수가 이렇게 말했다.
"언젠가는 interror가 나올 것이니 , 두가지 경우를 대비해야 할 것이다."
이 상자를 받은 변수는 상자로부터 int가 나올 때를 대비해 then메소드를 , error가 나올 경우를 대비해 catchError메소드를 준비해야 한다.

import 'dart:async';

Future<int> futureNumber() {
  // 3초 후 100이 상자에서 나옵니다
  return Future<int>.delayed(Duration(seconds: 3), () {
    return 100;
  });
}

void main() {
  // future 라는 변수에서 미래에(3초 후에) int가 나올 것입니다
  Future<int> future = futureNumber();

  future.then((val) {
    // int가 나오면 해당 값을 출력
    print('val: $val');
  }).catchError((error) {
    // error가 해당 에러를 출력
    print('error: $error');
  });

  print('기다리는 중');
}

-> 3초 후에 futureint로 바뀌는 것이 아님 Future<int> 계속해서 유지

그렇다면 어떻게 Future<int> 에서 나오는 값을 다루는 걸까?

--> future.then()을 통해 다룬다 , valFuture<int> 상자가 열렸을 때 나오는 값이 들어갈 것이므로 100 이라고 출력되는 것이다.

비동기로 처리하지 않았다면 Future<int> 에서 값이 나올 때 까지 코드는 실행되지 않고 정지상태로 있을 것이다.


3. async / await

  1. await 키워드를 사용한 함수는 무조건 async 함수이어야 한다.
  2. async 함수는 무조건 Future를 반환해야 한다.
Future functionName() async {
  ...
  await someFunction();
  ...
}

4. async / await 을 사용해야하는 이유 !!!


1. 동기식 코드

Future<ProcessedData> createData() {
  return _loadFromDisk().then((id){
    return _fetchNetworkData(id);
  }).then((data){
    return ProcessedData(data);
  })
}

-> 다음 코드는 동기식으로 처리한 코드이다 명령 하나하나가 수행되면 다음단계로 넘어가는 then 을 사용하여 복잡한 코드를 보여준다.


2. 비동기식 코드

Future<ProcessedData> createDate() async {
  final id = await _loadFromDisk();
  final data = await _fetchNetworkData(id);
  return ProcessedData(data);
}

-> 다음 코드는 비동기식으로 처리한 코드이다. 1번 동기식 코드와 같은 작업을 하는 코드이지만 훨씬 간결하고 보기 좋은 코드이다. 마치 동기적으로 수행하는 코드인 것 같다.



5. 비동기식 코드가 동작하는 원리에 대해 살펴보자

  1. 가장 먼저 첫 번째 줄의 await 키워드가 붙은 _loadFromDisk()를 수행한다.
    await 키워드를 보고 함수의 수행을 멈춘다.

  2. 함수를 호출한 곳에 일단은 미리 Future<ProcessedData>를 return 하고
    나중에 함수가 완료되면 ProcessedData가 나올 것을 알린다.

  3. _loadFromDisk() 함수가 끝날 때 까지 기다리다가 함수의 수행이 완료되면 다음 줄을 수행하고 마찬가지로 await 키워드가 붙은 함수이기 때문에 위 과정과 같은 과정을 반복한다.

  4. 마지막으로 return ProcessedData(data) 를 수행하고 호출한 곳에서 미리 return 받았던 Future<ProcessedData> 에서 ProcessedData가 나오게 된다.


6. 언제 await 을 사용해야 할까?

1. await 키워드를 사용하지 않았을 때

Future<String> helloWorld() {
  // 3초 후에 Future<String> 에서 "Hello World" 가 나옵니다
  return Future.delayed(Duration(seconds: 3), () {
    final hello = "Hello World";
    print(hello);
    return hello;
  });
}

void main() {
  final future = helloWorld();
  print(future);
}

실행 결과는 다음과 같다

Instance of 'Future<String>'
Hello World

이유가 무엇일까?

  1. 출력 순서가 위와 같은 이유는 await키워드를 사용하지 않았으므로 비동기적으로 처리된다. 따라서 3초가 되기 전에 main 내에 print(future) 가 수행된다.

  2. 첫번째 출력이 Instance of 'Future<String>'인 이유는 future로 반환된 값은 상자가 열렸을 때 나오는 값으로 바뀌는 것이 아니기 때문이다 future의 타입은 String으로 바뀌는 것이 아니라 계속 Future<String>이다.

2. await 키워드를 사용했을 때

Future<String> helloWorld() {
  return Future.delayed(Duration(seconds: 3), () {
    final hello = "Hello World";
    print(hello);
    return hello;
  });
}

void main() async {
  final future = await helloWorld();
  print(future);
}

실행 결과는 다음과 같다

Hello World
Hello World

이유가 무엇일까?

-> await을 사용하지 않았을 때와 달리 await키워드가 붙은 동작이 완료될 때 까지 함수를 정지시키고 기다리고 해당 동작이 끝나고 나면 곧 바로 결과를 넘겨주기 때문에 Future<String>으로 String을 얻을 수 있는 것이다.

0개의 댓글