조용한 담장

Dart : Asynchronous programming 본문

Dart

Dart : Asynchronous programming

iosroid 2019. 10. 24. 17:24

Dart : Asynchronous programming

Dart 의 비동기 프로그래밍.

Asynchrony support

비동기 프로그래밍은 Future or Stream 을 통해 지원한다.

아래 코드를 보면서 감을 잡아보자.

  1. main() 에서 첫번째 print() 를 출력하고 두번째 print() 에서 출력할 내용을 await createOrderMessage() 이 리턴할 때 까지 기다린다.

  2. createOrderMessage() 에서 var order 변수를 생성하고 값을 설정하기 위해 await getUserOrder() 의 리턴값을 기다린다.

  3. getUserOrder() 에서 Future.delayed() 가 4초 후 'Large Latte' 문자열을 리턴한다.

  4. await getUserOrder() 가 문자열을 리턴하면 createOrderMessage() 의 나머지 코드가 수행되어 'Your order is: $order' 문자열을 리턴한다.

  5. createOrderMessage() 이 리턴되어 기다리던 print() 동작이 수행되어 인자로 전달된 'Your order is: Large Latte' 문자열을 출력한다.

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

Future<String> getUserOrder() {
  // Imagine that this function is
  // more complex and slow.
  return
   Future.delayed(
     Duration(seconds: 4), () => 'Large Latte');
}

// Asynchronous
main() async {
  print('Fetching user order...');
  print(await createOrderMessage());
}

// 'Fetching user order...'
// 'Your order is: Large Latte'

Future class

An object representing a delayed computation.

A Future is used to represent a potential value, or error, that will be available at some time in the future.

Future 는 특정 코드가 비동기적으로 수행된 후 의 결과가 저장되면 콜백으로 알려주고 저장된 데이터를 읽을 수 있게 된다.

future 의 state 는 Uncompleted 와 Completed 로 나눠진다.

  • Uncompleted : 비동기로 수행되고 있는 함수, 코드가 완료되지 않아 결과나 error 를 기다리고 있는 상태이다.
  • Completed
    • Completing with a value : 비동기 함수, 코드 수행이 완료되어 정상적인 결과 값으로 Future 나 Future 를 가지고 있다.
    • Completing with an error : 비동기 함수, 코드 수행에 에러가 발생하여 에러값을 가지고 있다.

If a future doesn’t produce a usable value, then the future’s type is Future.

Future<void> 는 리턴값이 없는 void 함수를 수행할때 가지게 되는 Future 타입이다.

아래처럼 Future 의 데이터를 읽어야 하는 receiver 는 콜백 함수를 등록해서 결과값을 처리 한다.

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

위의 콜백함수와 그 결과 처리 코드를 좀 더 간결하게 쓸 수 있도록 async, await 를 제공한다.

async, await

async 로 표시된 함수는 내부 코드에서 await 키워드를 만나면 그 뒤의 동작을 비동기적으로 처리하고 결과를 기다리며 진행을 멈추고 Future 가 리턴되면 다시 동작을 수행한다.

async function

An async function is a function whose body is marked with the async modifier. Adding the async keyword to a function makes it return a Future.

함수명 뒤에 async 키워드를 가진 함수로 비동기적인 속성을 가지게 되고 그 결과로 Future 를 리턴한다.

Future<String> lookUpVersion() async => '1.0.0';

 

 

awaitasync function 내에서만 사용해야 한다.

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}
Future main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}

위 코드 동작을 보면, Future 를 리턴하는 async function 인 checkVersion()await 를 만나면 자신의 동작은 멈추고 비동기 적으로 lookUpVersion() 이 수행되며 그 결과(Future)가 리턴되면 var version 에 값을 저장하고 나머지 코드를 수행한다.

 

await 는 error 도 전달하는 데 try, catch, and finally 를 통해 처리한다.

try {
  version = await lookUpVersion();
} catch (e) {
  // React to inability to look up the version
}

 

async function 안에서 await 는 여러번 사용될 수 있다.

// wait three times.
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

 

Future class 의 메소드들을 then() 을 사용하여 구현하는 코드 보다 await/async 를 사용한 코드가 조금 더 간단하고 이해하기 좋은 장점이 있다.

PREFER async/await over using raw futures.

 

// async/await
Future<int> countActivePlayers(String teamName) async {
  try {
    var team = await downloadTeam(teamName);
    if (team == null) return 0;

    var players = await team.roster;
    return players.where((player) => player.isActive).length;
  } catch (e) {
    log.error(e);
    return 0;
  }
}

// Future methods
Future<int> countActivePlayers(String teamName) {
  return downloadTeam(teamName).then((team) {
    if (team == null) return Future.value(0);

    return team.roster.then((players) {
      return players.where((player) => player.isActive).length;
    });
  }).catchError((e) {
    log.error(e);
    return 0;
  });
}

async 를 쓰지 않아도 되는 경우엔 생략하자.

Future afterTwoThings(Future first, Future second) {
  return Future.wait([first, second]);
}

// bad
Future afterTwoThings(Future first, Future second) async {
  return Future.wait([first, second]);
}

위 예제의 afterTwoThings()async 없이도 동작이 동일하다.

 

사용해야 하는 예제로 아래의 세가지 경우를 참고한다.

 

// 1. await 를 사용하는 경우
Future usesAwait(Future later) async {
  print(await later);
}

// 2. 비동기적으로 error 를 리턴하는 경우.
//    Future.error() 메소드 보다 코드가 간단해 진다.
Future asyncError() async {
  throw 'Error!';
}

// 3. Future 를 통해 값을 전달하는 경우.
//    Future.value() 메소드 보다 코드가 간단해 진다.
Future asyncValue() async => 'value';

Future Examples

Future class 의 동작을 확인해본 간단한 snippet.

Future()

 

 

Future.value()

지정한 결과값을 가진 future 를 생성하는 constructor

 

 

Future.delayed()

특정 시간 후에 동작을 수행하는 future 를 생성하는 constructor

timeout() 메소드를 이용해 일부러 에러를 발생하는 예제 포함.

 

 

Future.forEach(Iterable elements, FutureOr action(T element))

elements 각각의 값을 가지고 순서대로 action 을 수행하여 최종 결과를 future 로 만드는 static method.

모든 action 이 정상 수행 되면 null 을 가지는 future 를 리턴하는 특성 때문에 결과값을 전달하는 용도로 사용할 수는 없다.

Future.wait() 는 값을 전달할 수 있다.

아래 코드에서 elements 의 순서대로 수행되나 결과의 순서는 각각의 비동기 수행의 결과에 따라 다를 것이다.

 

 

Future.wait(IterableFuture futures, { bool eagerError: false, void cleanUp(T successValue) })

Future.error('Future.error()'), 를 주석처리 하면 두번째 output 결과를 얻는다.

 

 

Future.doWhile(FutureOr action())

false 를 리턴할때 까지 action 을 반복 수행한다.

 

 

Reference

Language tour

Asynchronous programming: futures,async,await

Asynchronous programming: streams

dart:async library

Effective Dart

'Dart' 카테고리의 다른 글

Dart : Collections  (0) 2019.11.19
Dart : print  (0) 2019.11.04
Dart : Class  (0) 2019.10.02
Dart : Exceptions  (0) 2019.09.20
Dart : Control flow statements  (0) 2019.09.17
Comments