반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 자바 검증
  • 이펙티브 자바 item9
  • 싱글톤
  • prerendering
  • 객체복사
  • 메이븐 골
  • Next.js
  • next js app
  • 이펙티브자바
  • 슬로우 쿼리
  • 싱글턴
  • 자료구조
  • 자바스크립트
  • 스프링 검증
  • 자바
  • item7
  • pnpm
  • item9
  • 플라이웨이트패턴
  • Pre-rendering
  • 도커공식문서
  • 느린 쿼리
  • 자바스크립트 면접
  • 서버리스 컴퓨팅
  • 외래키 제약조건
  • 싱글톤 패턴
  • item8
  • Java
  • 참조 해제
  • NEXT JS
  • 토비의 스프링
  • rust
  • 작업기억공간
  • serverless computing
  • 이펙티브 자바
  • 러스트
  • Javadoc 자바독 자바주석 주석 Comment
  • bean Validation
  • 메이븐 라이프사이클
  • 프로그래머의 뇌
  • 디자인패턴
  • 알고리즘
  • MSSQL
  • 자바스크립트 인터뷰
  • 자바 디자인패턴
  • 추상 팩터리 패턴
  • try-with-resources
  • 빈 검증
  • 메이븐 페이즈
  • 팩터리 메서드 패턴

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

Dart

다트 비동기 프로그래밍 (Future, Async, Await)

2023. 10. 22. 21:17

배우는 것

  • 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 에 비교될 것이다.

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();
  }
}

다트 공식문서 원문

  • https://dart.dev/codelabs/async-await
반응형
저작자표시 비영리 (새창열림)

'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
    'Dart' 카테고리의 다른 글
    • Dart 의 Null-safe 언어적 특성
    • 다트의 비동기 지원 (Asynchronous Support)
    • 다트 (Dart) 언어의 생성자 (Constructors) 정리
    • 다트(Dart) 언어의 클래스(Class) 정리
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바