Async Function
async function은 비동기 함수이다. 말 그대로 함수의 작업이 끝나지 않아도 다음코드를 진행해 버린다. async function은 함수 리턴값으로 Future 클래스를 리턴한다. 이 Future클래스로 함수가 끝난 결과값을 처리할 수 있다.
아래 일반적은 sync function이 있다고 하자
String lookUpVersion() => '1.0.0';
이를 async로 바꾸면 아래처럼 함수에 async 키워드를 붙이고 Future클래스를 리턴해주면 된다
Future<String> lookUpVersion() async => '1.0.0';
위 함수는 String값을 리턴하기에 Future<String> 이 되었다. 만약 값을 리턴하지 않으면 Future<void> 라고 적어주면 된다.
Future 클래스
비동기 함수는 Future 클래스를 리턴한다. 이 Future 클래스는 미래에 비동기 작업이 끝나면 값을 받아오는 클래스이다
Future클래스에는 콜백을 2개 등록할 수 있다.
- 비동기 함수가 성공시 성공에대한 콜백
- 비동기 함수가 error가 날 시 exception을 일으키는 콜백
Future는 두가지의 state를 가진다.
- Uncompleted, 비동기 함수가 아직 끝나지 않은 상태
- Completed, 비동기 함수가 작업이 끝난 상태. 성공하면 value를, 실패하면 error 값을 가져온다
Completing with a value
Future<T>는 비동기 처리가 성공시 T타입의 값을 가져온다. 만약 T가 String이면 Future<String> 으로 적어주면 된다
값을 리턴하지 않을경우 Future<void> 라고 적으면 된다.
Completing with an error
만약 비동기 처리가 fail되면 Future는 에러값을 가지게 된다.
아래의 예제는 2초후에 값을 리턴하는 코드의 예제이다.
Future<void> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}
void main() {
fetchUserOrder();
print('Fetching user order...');
}
Fetching user order...
//2초후
Large Latte
Future의 then(), catchError() 함수
then() 함수를 통해 Future가 complete 되면 실행해줄 콜백을 등록할 수 있다.
또한 catchError() 함수를 통해 에러처리를 해줄 수 있다.
then().catchError은 try-catch의 async버전이라고 생각하면 된다.
HttpRequest.getString(url).then((String result) {
print(result);
}).catchError((e) {
// Handle or ignore the error.
});
Async와 Await를 Future와 함께 사용하기
Await 키워드는 말 그대로 비동기 작업을 기다리는 키워드이다
두가지만 기억하자
- async function을 정의하려면, 함수바디 앞에 async를 붙여준다
- await는 async 함수 안에서만 동작한다!
아래 예제를 보자
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>'
위 예제는 async와 await를 적용하지 않은 예제이다. 특이하게도 createOrderMessage()함수는 String을 리턴하는데 리턴값이 _Future<String>이 된 걸 알 수 있다.
아마 order값이 Future이고 String과 결합되어 Future로 바뀐걸로 추측된다.(구글놈들이 이런것좀 써달라고...)
자 이제 async와 await를 달아주자.
먼저 결과물에서 알 수 있듯이 main함수에서도 Future를 기다려주어야 한다. 고로 await를 달자
print(await createOrderMessage());
아까 주의사항이 기억나는가? await는 async안에서만 놀아야된다. 고로 main함수에도 async를 달아주자
Future<void> main() async { ··· }
그럼 아래와 같은 코드가 작성된다.
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
여러개의 Future waiting하기
Future.wait() 함수를 이용하면 여러개의 Future를 waiting 할 수 있다.
Future<void> deleteLotsOfFiles() async => ...
Future<void> copyLotsOfFiles() async => ...
Future<void> checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
Future channing 하기
then() 메소드는 Future 클래스를 리턴한다. 이를 통해 연속해서 then() 함수로 콜백을 등록하여 여러개의 작업을 순서대로 실행시킬 수 있다.
Future result = costlyQuery(url);
result
.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception) {
/* Handle exception... */
});
위 코드는 아래와 같다
try {
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
} catch (e) {
/* Handle exception... */
}
Stream 클래스
데이터의 흐름을 비동기적으로 받아온다. 가령 예를들어 스마트폰 액정을 터치하거나 컴퓨터의 화면에서 마우스를 클릭할때 정보를 받아와 처리하는 클래스이다. Stream.listen 에 콜백을 등록하여 스트림 데이터를 처리할 수 있다.
await for 로 Stream 데이터 다루기
Stream 데이터를 다루는 방법은 두가지가 있다
- async와 await for을 이용하여 Stream 데이터 처리
- Stream API를 이용하는 방법, listen()
지금은 await for을 이용하여 스트림 데이터를 다루는 방법을 설명하겠다
사용법은 아래와 같다
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
주의사항 2가지만 기억하자
- expression의 데이터타입은 반드시 Stream 이여야 한다!!
- await for은 async 함수 안에서 실행되어야 한다!! (await는 async 안에서만 놀아야함)
await for은 아래와 같은 순서로 작동한다.
- Stream으로부터 데이터를 받을때 까지 기다린다.
- Stream으로부터 데이저를 전달받으면 전달받은 데이터로 for loop를 실행한다.
- 1번과 2번을 Stream이 close 될 때 까지 반복한다
아래는 request 스트림을 처리하는 예제이다. 만약 await for을 main함수에 쓰고싶으면 main 함수에 async를 붙여준다
Future<void> main() async {
// ...
await for (final request in requestServer) {
handleRequest(request);
}
// ...
}
listen()을 통한 Stream 데이터 다루기
Stream 데이터는 listen() 함수에 콜백을 달아 처리도 가능하다.
아래 예제는 버튼클릭에 대한 핸들러를 listen()에 등록해주는 예제이다.
// Add an event handler to a button.
submitButton.onClick.listen((e) {
// When the button is clicked, it runs this code.
submitData();
});
Stream에서 일부 이벤트만 다루기
Stream 클래스는 first, last, single이라는 프로퍼티를 가지고 있다. 이 프로퍼티로 이벤트 하나만 처리 할 수 있다.
single 프로퍼티는 Stream이 보내주는 데이터가 딱 하나일때 사용한다. 1개 이상이면 에러를 일으킨다.
만약 Stream 이벤트를 handling 하기 전에 test해보고 싶다면 firstWhere(), lastWhere(), singleWhere() 함수를 사용한다.
- firstWhere() : 테스트를 만족하는 요소중 첫번째 요소만 리턴
- lastWhere() : 테스트를 만족하는 요소중 마지막 요소 리턴
- singleWhere() : 테스트를 만족하는 요소가 하나면 리턴, 2개이상일경우 stateError를 일으킴
또한 skip(), skipWhile(), take(), takeWhile()로 일부 이벤트를 스킵하거나 일부 부분만 처리할 수 있다.
Stream 데이터 타입변형
transform()을 이용하여 데이터의 타입을 변경할 수 있다.
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
Stream의 에러 처리
await for loop는 try and catch 구문으로 예외처리 한다
Future<void> readFileAwaitFor() async {
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
try {
await for (final line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
}
listen() API를 이용한다면 성공했을때는 onDone에 콜백을, 에러가 다면 onError에 에러처리 콜백을 달아준다
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
inputStream
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((String line) {
print('Got ${line.length} characters from stream');
}, onDone: () {
print('file is now closed');
}, onError: (e) {
print(e);
});
'Dart > 기초' 카테고리의 다른 글
[Dart] Generator 제너레이터 (0) | 2021.10.15 |
---|---|
[Dart] 익명함수(Anonymous function), 람다함수(lamda) (0) | 2021.10.15 |
[Dart] deferred as 로 라이브러리 나중에 로딩하기 (0) | 2021.10.15 |
[Dart] var과 dynamic (0) | 2021.10.15 |
[Dart] mixin 클래스 (0) | 2021.10.15 |
댓글