배운것들을 정리합니다.
三昧境

Flutter/Dart Language

[플러터 flutter] 비동기 / 동기 async, awiat, future은 무엇인가?

ujo_orr 2024. 7. 4. 23:23

Future은 비동기 작업을 처리하는 데 사용되는 클래스입니다.
비동기 작업은 즉시 완료되지 않고 일정 시간이 걸리는 작업을 말하며, Future는 이러한 작업의 완료 또는 실패 결과를 나타내는 객체입니다.

여기서 동기와 비동기란


비동기 (asynchronous) / 동기 (synchronous)

위 사진을 참고하여 보면

 

  • 동기 (Synchronous)
    • 한 작업이 완료될 때까지 다음 작업이 시작되지 않습니다.
    • 간단하고 직관적이지만, 시간이 오래 걸리는 작업일 경우 비효율 적입니다.
void main() {
  print('시작');
  performTask();
  print('끝');
}

void performTask() {
  print('실행중');
}

// 차례대로 출력
시작
실행중
끝

 

 

  • 비동기 (Asynchronous)
    • 한 작업이 진행되는 동안 다른 작업을 동시에 실행할 수 있습니다.
    • 복잡하지만 시간소모가 적어 효율적입니다. 
void main() async {
  print('시작');
  await performTask();
  print('끝');
}

Future<void> performTask() async {
  await Future.delayed(Duration(seconds: 2));
  print('실행중');
}

// 차례대로
시작
(2초 기다림)
실행중
끝

로 볼수 있습니다. 그리고 위 코드에서

void main() async {
  print('시작');
  await performTask();
  print('끝');
}

performTask() async {
  await Future.delayed(Duration(seconds: 2));
  print('실행중');
}

위 코드처럼 Future<void>는 생략할 수 있습니다.

Dart에서는 async 키워드를 보고 자동으로 Future<>로 간주합니다.
허나, Future<void> 처럼 반환타입을 명시하는 것이 코드를 읽을 때 Future를 반환하는 것을 정확히 알 수 있기 때문에
적어 주는게 좋습니다.


Future

Future는 비동기 함수가 호출될 때 반환됩니다.
보통은 async/await와 함께 사용하지만 Future 단독으로도 사용될 수 있습니다.
함수 가장앞에 반환 타입과 함께 표기됩니다.

Future<String> performTask() {
  return Future.delayed(Duration(seconds: 2), () {
    return '실행중';
  });
}

 


 

async / await

async / await 키워드를 사용하면 비동기 코드를 더 쉽게 작성할 수 있습니다.
async 함수는 항상 Future를 반환하며
await 키워드는 해당 Future가 완료될 때까지 기다립니다.

void main() async {
  print('시작');
  String result = await performTask();
  print(result);
  print('끝');
}

 

  • async는 함수의 ()소괄호와 {}중괄호 사이에 입력하여야 합니다.
  • await는 기다려야할 변수 앞에 키워드를 입력하여야 합니다.
  • 두 개의 키워드는 함께 쓰여야 기능합니다.

여기서 드는 의문은 언제 어떻게 Future와 async/await를 사용하느냐입니다.

// async/await사용안함, return있음

void main() async {
  print('시작');
  String result = await performTask();
  print(result);
  print('끝');
}

Future<String> performTask() {
  return Future.delayed(Duration(seconds: 2), () {
    return '실행중';
  });
}
// async/await사용함, return없음

void main() async {
  print('시작');
  await performTask();
  print('끝');
}

Future<void> performTask() async {
  await Future.delayed(Duration(seconds: 2));
  print('실행중');
}
// async/await사용안함, return없음

void main() {
  print('시작');
  performTask().then((result) {
    print(result); // '실행중' 출력
    print('끝');
  });
}

Future<void> performTask() {
  return Future.delayed(Duration(seconds: 2)).then((_) {
    print('실행중');
  });
}

이 세 개의 코드는 똑같은 결과를 내놓습니다.

위 코드에서 Future는 async/await를 사용하지 않았고, String값인 '실행중'을 반환하도록 돼있습니다.
가운데 코드에서 Future는 async/await를 사용하였고, void로 아무것도 반환하지 않도록 돼있습니다.
아래 코드에서 Future는 async/await를 사용하지 않았고, void로 아무것도 반환하지 않도록 돼있습니다.

(then 함수는 Future 객체가 내포하고 있는 메서드로, 비동기 작업이 완료되었을 때 실행할 콜백 함수를 지정하는 데 사용됩니다.
thenFuture가 성공적으로 완료된 후에 호출되며, 완료된 Future의 결과를 콜백 함수의 인수로 전달합니다.)

결론을 말씀드리자면

  • async/await 사용
    • async와 await 키워드는 비동기 작업의 순서를 명확하게 하고, 코드의 가독성을 높이는 데 도움을 줍니다.
    • 그러므로 복잡한 비동기 로직을 처리할 때는 async와 await를 사용하는 것이 더 직관적이고 유연합니다.
  • Future만 사용
    • 단순한 비동기 작업에서는 Future 객체와 콜백 함수를 직접 사용하여 async와 await 없이 작성할 수 있습니다.

이렇듯 코드의 구성차이이며 복잡하거나 코드의 가독성을 높일 땐 async/await를 사용합니다.


왜 비동기 프로그래밍을 하는가

대표적으로 파일 읽기/쓰기, 네트워크 요청 등이 있습니다.

예를 들어

Image.network("https://blahblah.png")

이와 같은 코드는 네트워크를 통해 이미지를 불러오는 코드를 작성한다 하였을 때

비동기 형식을 사용한다면 화면에서 버튼, 상하단바는 다 보이는 상황인데 이미지만 보이지 않은 상황이 됩니다.
이를 비동기 작업을 하여 동기화를 시켜 

Future<Image> loadImage() async {
    return Image.network(
      "https://blahblah.png",
    );
  }

이미지의 로드가 완료될 때까지 나머지 작업을 멈출 수 있습니다.