Future, async, await

김석·2023년 4월 17일
0

Flutter

목록 보기
3/5

1. Future

  1. 비동기 처리를 위해 존재하는 자료형.
  2. 미래에 처리된 데이터가 담길 그릇. 당장이 아니라 미래에 처리된 데이터를 가지고 어떤 작업을 하기 위해 필요함.
  3. 네트워크를 통해 요청하고 응답을 받아올 때, 해당 요청을 받을 때까지 앱이 멈추면 안 된다. 따라서 네트워크 요청은 비동기 방식으로 진행됨. 네트워크 요청은 요청대로 흘러가고, 코드 진행은 알아서 내려감. 그 와중에 네트워크 요청의 응답이 온다면? 적절한 조치를 취해야 함. 이를 위해 future가 필요하다.
  4. 어떠한 기본 함수가 비동기로 동작하는지 비동기로 동작하는지 알고 있어야 앱의 흐름을 제대로 파악할 수 있다.

Future<int> futureNumber() {
  // 3초 후 100을 들고 있는 future 리턴
  return Future<int>.delayed(Duration(seconds: 3), () {
    return 100;
  });
}

void main() {
  Future<int> future = futureNumber();
  future.then((val) {
    print('val: $val');
  }).catchError((error) {
    print('error: $error');
  });

  print('기다리는 중');
}
  1. futureNumber 함수 실행.
  2. futureNumber가 Future를 return함. <-- 중요!
  3. 기다리는 중 출력.
  4. future가 100이라는 값을 가진 채로 처리됨.
  5. 3초 뒤 then이 불리고 100을 출력함.

then에서의 문법
> ()는 future로 감싸서 리턴한 매개변수.
{}에는 그 매개변수를 바탕으로 실행될 함수.


2. await, async

await: 해당 비동기 함수가 처리될 때까지 동기처럼 기다려라.
async: 나는 비동기 함수다!

규칙
1. 어떠한 함수 내부에서 await를 사용했다면, 그 함수는 async여야 한다.
-> 함수 내부에서 future 기다릴 놈이면 그 함수 자체도 비동기 함수로 작성해라. 성능을 위해 비동기를 사용하는데, await 때문에 모든 작업을 멈출 수 없기 때문이다.
2. async 함수는 무조건 future를 반환해야 한다.
-> 비동기 함수는 future를 반환해서 미래에 응답된 데이터를 적절히 처리해라.


void main() {
  Future<ProcessedData> data = createData();
  print(data);  
}

Future<ProcessedData> createData() async {
  final id = await _loadFromDisk();
  final data = await _fetchNetworkData(id);
  return ProcessedData(data);
}

async와 await를 사용하면 then보다 더 직관적이고 간결함.

  1. main에서 createData() 호출.
  2. createData에서 _loadFromDist()를 실행. await가 있으므로 createDate 내부의 동작은 멈춤.
  3. async 함수인 createData는 일단 호출된 곳인 main에 Future<ProcessedData>를 리턴. <-- 중요!
  4. print(data)가 실행됨.
출력: Instance of 'Future<ProcessedData>
  1. _loadFromDisk() 실행 결과 id에 저장, _fetchNetworkData(id) 실행, 동작 중단.
  2. _fetchNetworkData(id) 실행 결과 data에 저장, ProcessedData(data) 실행.
  3. ProcessedData(data) 실행 결과 리턴, main의 data라는 future 그릇에 ProcessedData(data)가 저장됨.

우리는 ProcessedData(data)가 완료된 data를 출력하고 싶다. 근데 createData 함수는 async 함수이기 때문에 처리되기 전에 main에서 출력된다. 이런 경우에 main에서 createData를 호출할 때 await를 사용하면 된다.


void main() {
  Future<ProcessedData> data = await createData();
  print(data);
}
  
Future<ProcessedData> createData() async {
  final id = await _loadFromDisk();
  final data = await _fetchNetworkData(id);
  return ProcessedData(data);
}
출력: ProcessedData(data)에 해당하는 값.

3. async/await를 사용하면 then을 사용할 필요가 없다?

대부분의 상황은 대체 가능하지만, then이 필요한 경우도 존재한다.

await는 수행 중인 함수의 동작을 아예 멈추지만, then은 수행 중인 함수의 동작을 멈추지 않고 이후 코드가 계속 실행되기 때문.


Future<String> helloWorld() {
  return Future.delayed(Duration(seconds: 3), () {
    return "Hello World";
  });
}

Future<String> byeByeWorld() {
  return Future.delayed(Duration(seconds: 3), () {
    return "Bye Bye World";
  });
}

Future<List<String>> world() async {
  final future1 = await helloWorld();
  final future2 = await byeByeWorld();

  print('(world) future1: $future1');
  print('(world) future2: $future2');
  print('(world) future와 관계 없는 print문, 빨리 수행될 수록 좋음');
  return [future1, future2];
}

void main() {
  final futures = world();
  print('(main) future: $futures');
}
  1. world 함수는 async 함수. main에서 await하지 않는다.
실행:
print('(main) future: $futures');

출력:
(main) future: Instance of 'Future<List<String>>' //0초
  1. world 안에서 future1, future2 각각 3초씩 await.
  2. 총 6초 후,
실행:
print('(world) future1: $future1');
print('(world) future2: $future2');
print('(world) future와 관계 없는 print문, 빨리 수행될 수록 좋음');

출력:
(world) future1: Hello World    // 6초
(world) future2: Bye Bye World  // 6초
(world) future와 관계 없는 print문, 빨리 수행될 수록 좋음 // 6초 

Future<String> helloWorld() async {
  return Future.delayed(Duration(seconds: 3), () {
    return "Hello World";
  });
}

Future<String> byeByeWorld() async {
  return Future.delayed(Duration(seconds: 3), () {
    return "Bye Bye World";
  });
}

List<Future<String>> world() {
  final future1 = helloWorld();
  future1.then((val) {
    print('(world) future1: $val');
  });
  
  final future2 = helloWorld();
  future2.then((val) {
    print('(world) future2: $val');
  });
  
  print('(world) future와 관계 없는 print문, 빨리 수행될 수록 좋음');

  return [future1, future2];
}

void main() {
  final futures = world();
  print('(main) future: $futures');
}
  1. world 함수는 async 함수가 아니다. world 함수에서 async 함수 helloWorld, byeByeWorld를 호출한다.
실행:
print('(main) future: $futures');

출력:
(main) future: Instance of 'Future<List<String>>' //0초
  1. world에서 [future1, future2]를 return한다.
  2. 총 0초 후, main에서 print한다.
실행:
print('(main) future: $futures');

출력":
(main) future: [Instance of 'Future<String>', Instance of 'Future<String>']  // 0초
  1. 총 3초 후, future1, future2의 then이 실행된다.
실행:
future1.then((val) {
  print('(world) future1: $val');
});
future2.then((val) {
  print('(world) future2: $val');
});

출력":
(world) future1: Hello World    // 3초
(world) future2: Bye Bye World  // 3초

await 때문에 생긴 불필요한 중단을 then을 사용해서 해결할 수 있었다.


출처

https://velog.io/@jintak0401/FlutterDart-%EC%97%90%EC%84%9C%EC%9D%98-Future-asyncawait
https://pcseob.tistory.com/8

profile
handsome

0개의 댓글