dart - 비동기 프로그래밍

jokil·2023년 12월 13일
0

플러터

목록 보기
11/15

동기 비동기 개념과 문법을 알아보자

gpt에게 동기와 비동기에 대해서 물어봤다.

vue로 웹개발을 했었던 나로서는 이해가 아주 잘되는 묘사다.

네이버를 예로 들면

네이버에는 날씨를 나타내주는 위치도 있고 광고를 보여주는 배너도 있다.

이게 동기 형식이었다면 광고 배너가 터지면 사이트 전부가 터진다.

하지만 비동기형식으로 만들었기에 광고가 터져도 다른곳은 터지지 않고 정상 작동을 하는걸 볼 수 있다.

그냥 지나쳤겠지만 모두들 한번씩 이런 경험이 있을 것이다.

나도 네이버에서 그런 경험이 있다.



ㅁ콜백함수
// gpt가 써준 코드

import 'dart:async';

// 인자로 user와 callback을 받음
void callSignupAPI(String user, Function callback) {
  print('회원 가입 API 호출');
  // 실제 API 호출 로직을 여기에 구현합니다.
  // 가정: API 호출이 2초 후에 완료됩니다.
  Future.delayed(Duration(seconds: 2), () {
    callback(user); // main에 있는 callSignupApi호출 user파라미터를 받음
  });
}

void saveToDB(String user, Function callback) {
  print('DB에 저장');
  // 실제 DB 저장 로직을 여기에 구현합니다.
  // 가정: DB 저장이 2초 후에 완료됩니다.
  Future.delayed(Duration(seconds: 2), () {
    callback(user); // main에 있는 saveToDB호출 user파라미터를 받음
  });
}

void sendEmail(String user, Function callback) {
  print('이메일 전송');
  // 실제 이메일 전송 로직을 여기에 구현합니다.
  // 가정: 이메일 전송이 2초 후에 완료됩니다.
  Future.delayed(Duration(seconds: 2), () {
    callback(user); // main에 있는 sendEmail호출 user파라미터를 받음
  });
}

void printSuccess(String user) {
  print('회원가입이 성공적으로 완료되었습니다: $user');
}

void main() {
  var user = 'new_user';
  
  // 콜하면 불러오는 애
  callSignupAPI(user, (user) {
    saveToDB(user, (user) {
      sendEmail(user, printSuccess);
    });
  });
}

dart에서 callback이라는 함수가 따로 있다는 것도 처음 알았는데

저렇게 쓰지 않아도

b함수에 a함수쓰고 파라미터 날려서 호출하면 그게 콜백이다. (일단 내가 지금 이해한걸로는 그렇다)



ㅁFuture
공식 문서 Future설명

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

Asynchronous programming: futures, async, await

Learn about and practice writing asynchronous code in DartPad!

dart.dev

- 기본 문법

Future<void> fetchUserOrder() {
  // 이 함수가 다른 서비스나 데이터베이스에서 사용자 정보를 가져오고 있다고 상상해보자
  // 2초뒤에 라지 라떼 정보가 출력됨
  return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}

void main() {
  // 함수 호출
  fetchUserOrder();
  // 사용자가 주문한 오더값 가져오는 중이라는 문구가 출력됨
  print('Fetching user order...');
}

공식문서에 있는 코드다

메인에서 fetchUserOrder를 호출하면 print가 먼저 출력돼고 Duration을 거치고 완료되는 future를 반환한다.



  • 익셉션 던지는 방법
// 비동기 함수
Future<void> fetchUserOrder() {
// 이 함수가 사용자 정보를 가져오지만 버그가 발생했다고 상상해보자.
  // 2초뒤에 실행되고 익셉션 던짐
  return Future.delayed(const Duration(seconds: 2),
      () => throw Exception('Logout failed: user ID is invalid'));
}

void main() {
  // 함수 실행
  fetchUserOrder();
  // 실행하면서 사용자 정보 가져온다는 문구가 출력 
  // 2초 뒤에 로그아웃 실패라는 문구를 뿌리는 익셉션이 떨어짐
  print('Fetching user order...');
}
공식 문서에서 가져온 Future 예시다. 

에러 처리하는 방법이다


  • Future의 then() 함수를 활용한 회원가입 3단계 main 함수코드
final user = {
 'email' : 'abc@abc.com',
 'password' : '123456',
 'name' : 'John Doe',
 };
 
 saveDb(user)
  .then((_) => sendEmail(user)) // sendEmail 호출
  .then((_) => getResult(user)) // getResult 호출
  .then((value) => print(value)); // value인자를 받아서 출력함
< 출처 : 오준석의 생존코딩 >

이 코드를 보면 함수는 결과를 then() 함수를 통해서 받을 수 있다.

then()함수로 전달되는 콜백 함수에 다음에 실행할 코드를 작성하면 된다.

다음 코드가 Future라면 계속해서 then()을 이어서 결과를 전달받을 수 있다.

불필요한 인자는 _를 쓰는 것이 관례이다.



  • 비동기 처리의 성공, 실패와 관계없이 실행할 수 있게 해주는 함수

.whenComplete(() => print('완료'));
< 출처 : 오준석의 생존코딩 >

테스트할 때 쓰면 좋을거같음

중간중간에 넣어주면 어디까지 실행됐는지 조사할 수 있을거같다.

  • then() 사용의 문제점

  • 콜백 보다는 편하지만 동기식 코드 보다는 결과 예측이 어렵다.

  • 단계가 많아지면 then()을 연결하는 체이닝 방식을 사용하는 것이 만만치 않다. ( 가독성 떨어지는 코드 반복, 읽고 싶지도 않은 then 난무)

  • 로직이 복잡해지면 적절한 예외처리하기에 용이하지 않다.

  • 궁금하다면 최대한 복잡한 코드를 찾아보자.

  • then은 정말 써야할 상황이 아니면 쓰지말자



- async - await 문법

< 출처 : 오준석의 생존코딩 >

// 타입을 반드시 써줘야한다.
Future<String> runIntheFuture() async {
    // await키워드는 해당 Future가 끝날 때까지 함수 실행을 기다린다.
    // world라는 데이터를 가지게됨
	var data = await Future.value('world');
    
    // hello world를 반환함 
    return 'hello $data';
}

이 구문은 통째로 외워야한다.

await은 async키워드가 있는 함수에서만 사용할 수 있다.



  • 병렬처리

< 출처 : 오준석의 생존코딩 >

병렬 처리는 동시에 여러가지 일을 진행하는 것이다.

Future 함수는 await 없이 사용하면 동시에 여러개를 실행할 수 있다.

Future.wait()은 모든 처리가 끝날때 까지 기다린다.
실제 상황에서 로드가 느리면 사용해보면 좋다.


// 동시에 수행할 함수들 정의
Future<int> getInt1() async {
 await Future.delayed(const Duration(seconds: 1));
 return 1;
}

Future<int> getInt2() async {
 await Future.delayed(const Duration(seconds: 1));
 return 2;
}

Future<int> getInt3() async {
 await Future.delayed(const Duration(seconds: 1));
 return 3;
}

Future<int> getInt4() async {
 await Future.delayed(const Duration(seconds: 1));
 return 4;
}

Future<int> getInt5() async {
 await Future.delayed(const Duration(seconds: 1));
 return 5;
}

Future<void> printParallelInts() async {
List<Future<int>> futures = [
// 비동기 객체들 
 getInt1(),
 getInt2(),
 getInt3(),
 getInt4(),
 getInt5(),
];

// futures가 다 실행될때까지 기다림 그 후 모든값이 results에 저장됨
List<int> results = await Future.wait(futures);

print(results);
}


void main() {
// 결과 : [1, 2, 3, 4, 5]
printParallelInts();
}
profile
주니어 개발자에서 점핑점핑

0개의 댓글