(Flutter/Dart) Async, Await, Future 비동기 다루기

The Complete 2020 Flutter Development Bootcamp with Dart - created by Angela Yu

이 포스트는 DSC HUFS에서 진행하는 Flutter 스터디를 바탕으로 정리한 내용입니다.

플러터에서 async, await, future로 비동기 다루기

Dart는 싱글 쓰레드 기반의 언어입니다. 하지만 Dart로 만들어진 Flutter에서는 비동기를 다룹니다. 어떻게 플러터가 싱글 쓰레드로 비동기 처리를 하는지 알아봤더니 event loop를 쓴다고 합니다. 놀랍지 않은게 Android, iOS에서도 이런 loop(aka. main loop)를 사용한다고 합니다. Javascript에서도 event loop를 사용합니다. 다음은 event loop를 Flutter 팀에서 설명한 영상합니다.

Event loop를 이해하기 전에 isolate의 개념의 설명이 필요할 것 같아서 isolate 설명을 먼저 정리했습니다.

Isolate 의미

쓰레드는 자신만의 isolate와 메모리를 갖습니다. 각 isolate는 자신만의 메모리와 event loop를 할당 받습니다. isolate는 새로운 ioslate을 만들 수 있고, 새로운 isolate는 새로운 메모리와 새로운 event loop를 할당받습니다. 여기서 새로운 isolate는 부모 isolate의 메모리에 접근할 수 없습니다. 이렇게 isolate는 서로 격리(isolated)되어있다는 특징을 갖습니다. 따라서 서로 메모리를 공유하지 않고, 메모리 Locking이 필요없다는 장점이 있습니다. 격리된 각 isolate 간의 통신을 하려고 하면 메시지를 주고 받아야합니다.

Event loop 설명

“An event loop is a background infinite loop which periodically wakes up and looks in the event queue for any tasks that need to run. If any exist, the event loops puts them onto the run stack if and only if the CPU is idle.”

출처: https://medium.com/flutter-community/futures-async-await-threading-in-flutter-baeeab1c1fe3

이벤트 루프는 이벤트 큐에 들어있는 태스크들을 차례로 실행하는 루프입니다. 여기서 말하는 태스크는 I/O나 사용자가 dart에서 작성하는 코드로 인해 발생하는 모든 이벤트들입니다.

비동기 처리가 필요한 이유

CPU와 비교해서 한참 느린 작업들이 있습니다. 예를 들면 File을 읽고 쓰거나, http 통신으로 데이터를 가져오거나 보내는 등이 있습니다. 이런 작업들을 기다리는 것은 시간 낭비입니다.

그래서 Dart에서는 아래와 같은 순서로 작업합니다

  1. 일반 작업을 수행한다.
  2. CPU밖에서 일어나는 느린 작업(disk IO, http request 등)을 기다리는 순간이 생긴다.
  3. 앞의 느린작업이 끝나는 것을 알려주는 Listner가 생성된다.
  4. Listner가 main thread에 리턴된다. 이 listner를 Dart에서는 Future라고 한다.
  5. Main thread는 하던 작업을 다시 수행한다.
  6. 기다리던 느린 작업이 끝났을 때, event loop는 끝난 것을 보고 연결된 메서드를 main thread에 올리고 해당 작업을 마무리한다.

여러분들이 해야하는 것은 Future 인스턴스를 만들고 그 Future를 다룰 코드를 작성하는 것입니다.

Future에서 data 가져오기

Future에서 결과값을 가져오는 방법은 두가지 입니다.
첫 번째는 then을 사용하는 것이고,
두 번째는 async, await 을 사용하는 것입니다.

then

아래는 future 인스턴스에 할당된 작업이 끝났을 때 handleValue(value) 함수가 실행되는 것을 나타냅니다. future 인스턴스는 Future<int> 인스턴스이기 때문에 then 이후에 나타나는 valueint 형이 됩니다.

Future<int> future = getFuture();
future.then((value) => handleValue(value))
      .catchError((error) => handleError(error));

await

await 을 사용하면 await 을 사용한 곳에서 future 작업이 끝날 때까지 기다립니다. 다음은 await을 사용하는 예시입니다.

var order = await fetchUserOrder();

하지만 await을 사용할 경우 다른 작업을 전부 멈추고, future 작업을 기다리기 때문에 앱의 성능에 악영향을 끼치게 됩니다. 이 때 async 를 사용합니다. 즉, 내부에서 await을 사용하는 함수는 async 함수가 되어야합니다.

async

함수가 비동기라는 것을 나타내기 위해 async를 사용해줍니다. 그리고 async 함수는 항상 Future<String> 인스턴스를 반환합니다.

Future<String>  createOrderMessage() async {
  var order = await fetchUserOrder();
  return 'Your order is: $order';
}

더 자세한 설명은 아래의 dart 공식 문서에서 볼 수 있습니다. 감사합니다.

https://dart.dev/codelabs/async-await

Share: Facebook