비동기 프로그래밍(Asynchronous Programming)
- 작업이 완료될 때까지 기다리지 않고, 미래의 특정 시점에 값을 반환한다.
- 결과값이 나올 때까지 멈춰 있지 않고, 수행할 수 있는 다른 모든 작업을 찾아서 수행한다.
- 다른 모든 작업이 끝나야만 수행한다.
- Dart에는 `Future`, `Stream` 클래스를 사용한다.
Future Class
Future<int> number = Future.value(1);
Future<String> name = Future.value('mark');
Future<bool> isOddNumber = Future.value(1.isOdd);
- `Future`를 사용하려면 타입을 정의할 때 `Future<T>`로 정의하면 된다.
- 하나의 작업에 대해 값이나 이벤트가 한 번 발생하는 단일 비동기 작업에 사용한다.
Future.delayed()
void introduce(String name) {
print('$name의 자기소개 시작 !');
Future.delayed(Duration(microseconds: 1), () {
print('안녕 ? 나는 0.000001초 지난 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('Lee');
introduce('Kim');
}
/*
Lee의 자기소개 시작 !
Lee의 자기소개 끝 !
Kim의 자기소개 시작 !
Kim의 자기소개 끝 !
-- 🚨 0.000001초가 지난 상황에서 모든 코드 실행이 다 끝났으면 Future 실행 --
안녕 ? 나는 0.000001초 지난 Lee
안녕 ? 나는 0.000001초 지난 Kim
*/
- 호출 순서
- `introduce('Lee');` 실행
- `print('Lee의 자기소개 시작 !');` 실행
- `Future.delayed(Duration(microseconds: 1), () {
print('안녕 ? 나는 0.000001초 지난 Lee~');
});` 는 0.000001초 기다림 - 0.000001초를 기다리는 중에 나머지 코드를 실행
- `print('Lee의 자기소개 끝 !');` 실행
- 분명 0.000001초는 지났지만 3번은 동작하지 않고 동작 할 수 있는 다른 코드를 찾음
- `introduce('Kim');` 실행
- `print('Kim의 자기소개 시작 !');` 실행
- `Future.delayed(Duration( micro seconds: 1), () {
print('안녕 ? 나는 0.000001초 지난 Kim~');
});` 는 0.000001초 기다림 - 0.000001초를 기다리는 중에 나머지 코드를 실행
- `print('Kim의 자기소개 끝 !');` 실행
- 모든 코드들이 다 실행 됐으므로 기다렸던 `Future`문이 순서대로 실행
- `Future.delayed(Duration(microseconds: 1), () {
print('안녕 ? 나는 0.000001초 지난 Lee~');
});` 실행 - `Future.delayed(Duration(microseconds: 1), () {
print('안녕 ? 나는 0.000001초 지난 Kim~');
});` 실행
async - await
기본 동작
async함수 내에서 비동기를 동기적으로 실행되도록 하는 키워드.
비동기를 수행하는 클래스에는 `await`키워드를, 비동기 코드를 랩핑하고 있는 함수에는 `async`키워드를 사용한다.
void introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('Lee');
}
/*
Lee의 자기소개 시작 !
안녕 ? 나는 2초 후의 Lee ~
Lee의 자기소개 끝 !
*/
- 호출 순서
- ` introduce('Lee');` 실행
- ` print('Lee의 자기소개 시작 !');` 실행
- `await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 Lee ~');
`}); 는 2초 기다림 - 2초 후에 print('안녕 ? 나는 Lee ~'); 실행
- `print('Lee의 자기소개 끝 !');` 실행
- `async` 함수 내에서 `await` 키워드를 만나면, 해당 지점부터 비동기 작업(Future)이 완료될 때까지 기다린 후에 다음 코드를 실행한다.
심화 동작1
void introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 $name ~');
});
print('await가 끝났으니 $name의 자기소개 끝 !');
}
void main() {
introduce('Lee');
introduce('Kim');
}
/*
Lee의 자기소개 시작 !
Kim의 자기소개 시작 !
-- 🚨 2초가 지난 상황에서 모든 코드 실행이 다 끝났으면 Future 실행 --
안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Lee ~
await가 끝났으니 Lee의 자기소개 끝 !
안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Kim ~
await가 끝났으니 Kim의 자기소개 끝 !
*/
- 호출 순서
- `introduce('Lee');`실행
- `print('Lee의 자기소개 시작 !');`실행
- `await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Lee ~');
});`는 `await` 키워드로 인해 비동기 작업(Future)이 완료될 때까지 기다린다. - 그래서 `print('await가 끝났으니 Lee의 자기소개 끝 !');`도 기다린다.
- 비동기가 동기처럼 동작하기 위해서는 `async`함수 내에서만 가능하다. `introduce('Lee');`내에서 비동기 작업(Future)을 기다리는 동안 동작할 다른 코드가 없으니 밖으로 나가 다른 코드들을 살핀다.
- 실행할 코드가 남아있으므로introduce('Kim');를 실행
- `print('Kim의 자기소개 시작 !');`실행
- `await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Kim ~');
});`는 `await` 키워드로 인해 비동기 작업(Future)이 완료될 때까지 기다린다. - 마찬가지로 `print('await가 끝났으니 Kim의 자기소개 끝 !');`도 기다린다.
- `introduce('Kim');`내에서 비동기 작업(Future)을 기다리는 동안 동작할 다른 코드가 없으니 밖으로 나가 다른 코드들을 살핀다.
- 동작할 수 있는 다른 코드가 없으니 비동기 작업(Future)을 기다렸다가 완료가 되면 아까 진행되지 못했던 코드들이 다시 진행된다.
- `await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Lee ~');
});`실행 - `print('await가 끝났으니 Lee의 자기소개 끝 !');`실행
- `print('await가 끝났으니 Kim의 자기소개 끝 !');`실행
심화 동작2
심화 동작1에서 5번, 6번 순서가 가능했던 이유는
- `await`키워드는 `async`함수내에서만 동기적으로 동작할 뿐, 그 외 코드에는 비동기로 동작한다는 특징과
- `aysnc`키워드를 사용한 함수도 암시적으로 Future가 되는 특징 때문이다.
그렇다면 심화 동작1도 동기적으로 동작시켜 보자.
Future<void> introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 $name ~');
});
print('await가 끝났으니 $name의 자기소개 끝 !');
}
void main() async {
await introduce('Lee');
await introduce('Kim');
}
/*
Lee의 자기소개 시작 !
안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Lee ~
await가 끝났으니 Lee의 자기소개 끝 !
Kim의 자기소개 시작 !
안녕 ? 나는 2초도 지났고 모든 코드의 동작이 끝난 후의 Kim ~
await가 끝났으니 Kim의 자기소개 끝 !
*/
- `aysnc`키워드를 사용한 함수도 암시적으로 `Future`가 된다 했으니 `await`키워드를 붙여 호출할 수 있다.
- 암시적인 `Future`이므로 명시적으로 변경하기 위해 `introduce async`함수의 타입을 void는 그대로 가져가고 Future<T> 타입을 이용하여 `Future<void>`로 변경한다.
- 후에 `await`키워드를 사용해 `introduce`함수를 실행한다.
- 이 때, `await`키워드는 `async`함수 내에서만 사용가능하므로 `main`함수에 `async`키워드를 사용하면 된다.
비동기 함수 만들기
심화 동작2에서 배운 '`aysnc`키워드를 사용한 함수는 암시적으로 Future가 되는 특징'을 이용해 일반 함수를 비동기 함수로 만들 수 있다.
Future<String> fetchData() async {
return '데이터 로드';
}
void main() {
fetchData().then((data) {
print(data);
});
print('비동기 작업 시작');
}
- 일반 함수인 `fetchData`에 `async`키워드를 사용해 `Future`로 만든다.
- `async`키워드만 있으면 반환 타입을 입력하지 않아도 타입 추론으로 `Future<dynamic>`이 반환 타입이 되기 때문에 비동기 함수가 된다. 그러나 명시적으로 입력하는 것이 좋다.
- `Future`가 되었으니 반환 타입도 `Future<T>`형태가 된다.
- 반환을 `String`타입으로 하고 있으므로 `fetchData`함수의 반환 타입은 `Future<String>`이 된다.
마무리
모든 프로그래밍 언어에서 비동기 작업은 조금 난이도가 있는 편인 것 같다.
그렇지만 이것을 알아야 서버와 통신하는 멋진 앱을 만들 수 있으므로 어려워도 가치가 있지 않을까?
Dart 언어 문법을 다 알고난 후에 빨리 Flutter로 앱을 개발하고 싶다.
'TIL(Today I Learned)' 카테고리의 다른 글
[TIL-012] 내가 모르는 메서드를 잘 사용할 수 있는 이유 (0) | 2025.03.19 |
---|---|
[TIL-011] List<String> → List<int> 타입 변환 오류 해결 방법 (0) | 2025.03.17 |
[TIL-009] Dart 실전 문법 파헤치기 - Cascade 연산자 (0) | 2025.03.14 |
[TIL-008] Dart 실전 문법 파헤치기 - 타입 변환 메서드 (0) | 2025.03.12 |
[TIL-007] Dart로 콘솔 쇼핑몰 만들기-2 (0) | 2025.03.11 |