❮
[번역] Dart 비동기 프로그래밍 : futures, async, await
20190923
- 언제, 어떻게 async와 await를 사용하는가?
- async와 await로 실행 순서에 영향을 주는 방법.
- async 함수에 try-catch 문을 사용해서 비동기 에러를 처리하는 법.
비동기 코드의 중요성
비동기 작업은 다른 작업들이 비동기 작업이 완료 되는 것을 기다리는 동안에 하던 것을 완성하도록 합니다.
비동기 작업의 예
- 네트워크에서 데이터를 가져오기
- 데이터베이스 연산
- 파일에서 데이터를 읽기
비동기 작업을 위해 Future 클래스와 async, await 키워드를 사용합니다.
용어 정리
동기 작업(synchorous operation) | 동기 작업은 다른 작업이 실행되는 것을 막습니다. |
동기 함수(synchorous function) | 동기 함수는 동기 작업만을 수행합니다. |
비동기 작업(asynchorous operation) | 초기화되고 나면, 다른 작업을 비동기 작업이 끝나기 전에도 실행할 수 있습니다. |
비동기 함수(asynchorous function) | 비동기 함수는 최소 하나의 비동기 작업을 수행하고, 동기 작업도 수행할 수 있습니다. |
Future
future는 Future 클래스의 인스턴스 입니다. future는 비동기 작업의 결과를 2개의 상태로 표현 합니다. 상태에는 완성(completed)된 상태와, 미완성(uncompleted) 상태로 나뉩니다.
미완성(uncompleted) 상태는 값을 만들어 내기 전의 future의 상태를 말합니다.
미완성 Uncompleted
비동기 함수를 호출하면, 미완성 future 를 리턴합니다. future는 함수의 비동기 작업이 끝나거나 에러를 던지는 것을 기다립니다.
완성 Completed
비동기 작업이 성공하면, future는 값을 완성하게 됩니다. 작업이 실패하면 에러로 완성됩니다.
값으로 완성Future 타입의 future는 T 타입 값으로 완성됩니다. 사용가능한 값을 만들지 않는 경우에는 Future 값을 사용합니다.
에러로 완성 비동기 작업이 어떤 이유로 실패하면 에러로 future가 완성됩니다.
예시 : 값으로 완성
1
2
3
4
5
6
7
8
9 | future Future<void> getUserOrder() {
// Imagine that this function is fetching user info from another service or database
return Future.delayed(Duration(seconds: 3), () => print('Large Latte'));
}
main() {
getUserOrder();
print('Fetching user order...');
}
|
Fetching user order...
Large Latte
getUserOrder() 가 실행되면 값이 리턴되는 것이 아니라 3초를 기다렸다가 print('Large Latte') 를 하는 future가 리턴됩니다.
예시 : 에러로 완성
1
2
3
4
5
6
7
8
9
10 | Future<void> getUserOrder() {
// Imagine that this function is fetching user info but encounters a bug
return Future.delayed(Duration(seconds: 3),
() => throw Exception('Logout failed: user ID is invalid'));
}
main() {
getUserOrder();
print('Fetching user order...');
}
|
Fetching user order...
Uncaught Error: Exception: Logout failed: user ID is invalid
3초 뒤에 에러 메시지가 나옵니다.
async and await
async,
await 은
함수를 정의하고 그 결과를 사용하기 위한 선언적인(declarative) 방법을 제공합니다.
- async 함수를 정의하려면 함수 몸체 전에 async 를 추가합니다.
- await 키워드는 async 함수에서만 사용할 수 있습니다.
비동기 main 함수
Dart에서는 타입 추론을 하지만 제품 단계 코드에서는 타입을 지정하는 것을 추천합니다.
1 | Future<void> main() async {}
|
async 함수에서는 await 키워드를 사용해서 future가 완성되는 것을 기다릴 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 | // Synchronous
String createOrderMessage() {
var order = 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');
}
// Synchronous
main() {
print('Fetching user order...');
print(createOrderMessage());
}
// 'Fetching user order...'
// 'Your order is: Instance of _Future'
// 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'
|
- createOrderMessage 함수의 리턴 타입을 String에서 Future 으로 바꿈
- async 키워드를 통해서 createOrderMessage, main 함수가 비동기 함수가 됨.
- 비동기문의 완성된 결과를 받기 위해서 await 키워드를 getUserOrder와 createOrderMessage에 사용함. 즉, Future를 리턴하는 함수들을 호출할 때 await를 붙임.
async 와 await
첫 await 키워드가 나올 때까지는 async 함수는 동기 실행을 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | import 'dart:async';
void createOrderMessage () async {
print('Awaiting user order...');
var order = await getUserOrder();
print('Your order is: $order');
}
Future<String> getUserOrder() {
// Imagine that this function is more complex and slow.
return Future.delayed(Duration(seconds: 4), () => 'Large Latte');
}
main() async {
countSeconds(4);
await createOrderMessage();
}
// You can ignore this function - it's here to visualize delay time in this example.
void countSeconds(s) {
for( var i = 1 ; i <= s; i++ ) {
Future.delayed(Duration(seconds: i), () => print(i));
}
}
|
에러 다루기
try-catch 를 사용합니다.
1
2
3
4
5
6 | try {
var order = await getUserOrder();
print('Awaiting user order...');
} catch (err) {
print('Caught error: $err');
}
|