배우는 것
async
,await
의 적절한 활용async
,await
사용이 실행 순서에 미치는 영향try-catch
표현식을 사용하여 비동기 호출에서 발생하는 오류를 해결하는 방법
비동기 연산이 주로 사용되는 곳
- 네트워크를 통해 데이터 가져오기
- 데이터베이스에 데이터 쓰기
- 파일에서 데이터 읽기
주로 외부 리소스에서 무언가를 읽어오거나 무언가를 쓰는 경우 네트워크 비용 때문에 병목이 생긴다.
이 병목을 '비동기' 라는 방식을 통해 백그라운드 작업화 시켜 사용자에게 말끔한 사용자 경험을 주어 최대한 해결한다.
비동기 연산이 주는 일반적인 결과 타입
Future
: 작은 파일의 경우 한번에 가져올 수 있다.Stream
: 커다란 파일의 경우 특정한 단위로 잘라서 가져와야 할 수 있다.
잘못된 예제 1
fetchUserOrder()
내부에 있는Future
는 내부적으로 비동기 작업을 수행하는 인스턴스이다.- 그런
Future
를 반환받고 출력시키기 때문에 출력 결과가 조금 이상하다. Your order is: Large Latte
를 출력시키고 싶었다면,String
타입을 반환받아야 하며, 비동기 작업을 기다려주어야 한다.
String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
void main() {
print(createOrderMessage());
}
// 출력 결과
/*
Your order is: Instance of '_Future<String>'
*/
- 자바스크립트에서도
async
함수의 결과를await
없이 사용하려 하면Promise
객체만 받게 된다.
async function asyncOne() {
return 1;
}
function main() {
console.log(asyncOne()); // Promise {<fulfilled>: 1}
}
main();
async function asyncOne() {
return 1;
}
async function main() {
console.log(await asyncOne()); // 1
}
main();
용어 정리
- 동기 연산 (synchronous operation): 연산이 완료될 때까지 다른 연산이 실행되는 것을 차단함 (순서 보장)
- 동기 함수 (synchronous function): 동기 연산만 수행하는 함수
- 비동기 연산 (asynchronous operation): 연산이 완료될 때까지 다른 연산이 실행되는 것을 허가함 (순서 보장 안함)
- 비동기 함수 (asynchronous function): 하나 이상의 비동기 작업과 함께 다른 동기 작업도 수행
future 란?
- 소문자 f 인 future 는 Future 클래스 의 인스턴스를 말한다.
- future 는 비동기 작업의 결과를 표현한다.
completed
혹은uncompleted
의 결과를 갖는다.- JS 의
fulfilled
혹은rejected
는completed
에 비교되고 - JS 의
pending
은uncompleted
에 비교될 것이다.
- JS 의
Uncompleted
- 비동기 함수를 호출하면,
uncompleted
상태가 된다. - future 는 비동기 연산이 끝나거나 에러를 던지길 기다린다.
Completed
값을 가진 Completed
: 비동기 연산이 성공하면, future 는 값을 가진completed
상태가 된다.에러를 가진 Completed
: 비동기 연산이 실패하면, future 는 에러를 가진completed
상태가 된다.
Future
예제 1: 입문 (Introduction)
Fetching user order...
과Large latte
중 어떤 것이 먼저 출력될까?
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info from another service or database.
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}
void main() {
fetchUserOrder();
print('Fetching user order...');
}
// 출력 결과
/*
'Fetching user order...'
(2초 슬립)
'Large Latte'
*/
Future
예제 2: 에러 (Completing with an error)
- 위에서 소개한
에러를 가진 Completed
의 예시이다.
Future<void> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 2),
() => throw Exception('Logout failed: user ID is invalid'));
}
void main() {
fetchUserOrder();
print('Fetching user order...');
}
// 출력 결과
/*
Fetching user order...
Uncaught Error: Exception: Logout failed: user ID is invalid
*/
잠깐 리뷰
- future 를 반환하는 함수를 호출하면, 해야할 일을 큐에 등록하고 uncompleted future 를 반환한다.
- future 연산이 종료되면, completed with value 혹은 completed with an error 중 하나의 상태가 된다.
async
와 await
을 이용하기
async
로 비동기 함수를 정의하고await
으로 결과를 사용할 수 있는 선언적 방법을 제공한다.
사용 방법
- 함수의 바디 앞에
async
키워드를 붙인다. async
를 붙이는 순간 반환 타입이Future<void>
로 사실상 변하게 된다.async
키워드를 붙이면void
혹은Future
타입을 반환해야 한다.
Future
를 반환하지 않으면 아래와 같은 에러를 만나게 된다.
Error: Functions marked 'async' must have a return type assignable to 'Future'.
Future<T> main() async { ··· }
await
키워드는async
함수 내부에서만 동작한다.- 함께 사용하면 동기 예제와 매우 유사한 코드를 생성한다.
void anyAsyncFunction() async {
print(await createOrderMessage());
}
예제1: async
, await
을 사용하지 않은 경우
String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
void main() {
print('Fetching user order...');
print(createOrderMessage());
}
// 출력 결과:
/*
Fetching user order...
Your order is: Instance of '_Future<String>'
*/
예제2: async
, await
을 사용한 경우
Future<String> createOrderMessage() async {
var order = await fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
Future<void> main() async {
print('Fetching user order...');
print(await createOrderMessage());
}
// 출력 결과:
/*
Fetching user order...
Your order is: Large Latte
*/
예제 1과 2의 차이
createOrderMessage()
함수의 반환 타입이String
에서Future<String>
으로 변했다.async
키워드들이 함수 바디 시작 전에 나타났다.- 함수의 반환 타입을
Future<T>
로 만든다.
- 함수의 반환 타입을
await
키워드들이 비동기 함수를 호출하기 전에 나타났다.- 반환 값이
Completed
되기 까지 기다린다.
- 반환 값이
실행 흐름
async
함수는await
키워드를 만나기 전까지는 모두 동기로 즉시 실행된다.- 아래 코드의 출력 결과를 예측해보라
Future<void> printOrderMessage() async {
print('Awaiting user order...');
var order = await fetchUserOrder();
print('Your order is: $order');
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex and slow.
return Future.delayed(const Duration(seconds: 4), () => 'Large Latte');
}
void main() async {
countSeconds(4);
await printOrderMessage();
}
// You can ignore this function - it's here to visualize delay time in this example.
void countSeconds(int s) {
for (var i = 1; i <= s; i++) {
Future.delayed(Duration(seconds: i), () => print(i));
}
}
// 출력 결과
/*
Awaiting user order...
1
2
3
4
Your order is Large Latte
*/
문제
- 다트 공식 가이드 에 문제가 있으니 풀어보면 좋음 (매우 쉬움)
// Part 1
// You can call the provided async function fetchRole()
// to return the user role.
Future<String> reportUserRole() async {
String role = await fetchRole();
return 'User role: $role';
}
// Part 2
// Implement reportLogins here
// You can call the provided async function fetchLoginAmount()
// to return the number of times that the user has logged in.
Future<String> reportLogins() async {
int loginAmount = await fetchLoginAmount();
return 'Total number of logins: $loginAmount';
}
에러 핸들링 하는 방법
try ... catch
를 통해 에러 핸들링 가능
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
} catch (err) {
print('Caught error: $err');
}
에러 핸들링 예제 1
- 중간에 에러가 던져져
print(order)
까지 도달하지 못한다.
Future<void> printOrderMessage() async {
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
print(order);
} catch (err) {
print('Caught error: $err');
}
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex.
var str = Future.delayed(
const Duration(seconds: 4),
() => throw 'Cannot locate user order');
return str;
}
void main() async {
await printOrderMessage();
}
// 출력 결과
/*
Awaiting user order...
Caught error: Cannot locate user order
*/
에러 핸들링 문제
- 다트 공식문서 에서 체험해볼 수 있다.
// Implement changeUsername here
Future<String> changeUsername() async {
try {
var newUsername = await fetchNewUsername();
return 'changeUsername(): "$newUsername"';
} catch(e) {
return e.toString();
}
}
마지막 최종 문제
- 다트 공식문서 에서 체험해볼 수 있다.
// Part 1
String addHello(String username) {
return 'Hello $username';
}
// Part 2
// You can call the provided async function fetchUsername()
// to return the username.
Future<String> greetUser() async {
String username = await fetchUsername();
return addHello(username);
}
// Part 3
// You can call the provided async function logoutUser()
// to log out the user.
Future<String> sayGoodbye() async {
try {
String username = await logoutUser();
return '$username Thanks, see you next time';
} catch(e) {
return e.toString();
}
}
다트 공식문서 원문
반응형
'Dart' 카테고리의 다른 글
Dart 의 Null-safe 언어적 특성 (0) | 2023.12.15 |
---|---|
다트의 비동기 지원 (Asynchronous Support) (0) | 2023.10.22 |
다트 (Dart) 언어의 생성자 (Constructors) 정리 (0) | 2023.10.13 |
다트(Dart) 언어의 클래스(Class) 정리 (0) | 2023.10.12 |
다트(Dart) 언어의 클래스 제어자 (Class modifier) 정리 (0) | 2023.10.12 |