Dart에서 Stream을 사용하기 위해 필요한 기본적인 내용에 대해서 알아보겠습니다.
1. Stream
Stream은 비동기적인 이벤트의 흐름이며, 0개 또는 다수의 이벤트가 전달합니다.
Stream에 Listener를 등록하여 데이터를 수신할 수 있으며, Listener는 0개 또는 1개 이상이 될 수 있습니다.
Future와 차이점
Future도 비동기 프로그래밍에 사용되며, Stream과 동일하게 미래에 생성되는 이벤트를 얻을 때 사용됩니다.
하지만 Future와 Stream의 차이점이 있습니다.
Future의 경우 1개의 데이터 값만 가질 수 있으며, Stream 처럼 다수의 데이터를 갖고 있을 수 없습니다. 그리고 Stream의 경우 다수의 Listener가 있을 수 있지만 Future는 한곳에서만 읽을 수 있습니다.
2. Stream 이벤트 생성 및 수신
Iterator의 데이터로 Stream 생성
Stream<Type>.fromIterable()
은 Type 데이터를 갖고 있는 Iterator의 요소들을 순차적으로 Stream 이벤트를 생성합니다.
그리고 Stream.listen(func)
으로 Stream으로 전달되는 데이터를 func 함수에서 처리하도록 만들 수 있습니다.
void main() {
// Iterator의 요소들을 Stream 이벤트로 생성
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
// 이벤트 수신
stream.listen((data) {
print('data: $data');
});
}
Output:
data: 1
data: 2
data: 3
data: 4
data: 5
yield로 Stream 생성
yield
키워드를 이용하여 Stream을 생성할 수 있습니다.
yield는 return 키워드와 비슷한데, 함수를 종료하진 않고 Stream으로 데이터를 전달합니다.
아래 예제와 같이 Stream을 만들면, 1초 간격으로 Integer 값이 Stream으로 전달됩니다.
Stream에서 yield로 이벤트를 발생시킬 때 listen()
의 함수가 호출되어 작업이 수행됩니다.
Stream<int> makeStream() async* {
yield 1;
await Future.delayed(Duration(seconds:1));
yield 2;
await Future.delayed(Duration(seconds:1));
yield 3;
await Future.delayed(Duration(seconds:1));
yield 4;
await Future.delayed(Duration(seconds:1));
yield 5;
await Future.delayed(Duration(seconds:1));
}
void main() {
// Stream 생성
final stream = makeStream();
// 이벤트 수신
stream.listen((data) {
print('data: $data');
});
}
Output:
data: 1
data: 2
data: 3
data: 4
data: 5
3. where() : 이벤트 필터링
Stream.where(func)
는 func 함수의 리턴 값이 true인 이벤트만 읽습니다.
Stream.where(func).listen()
처럼 사용하면, 조건에 맞는 데이터만 listen()
으로 전달됩니다.
아래 예제는 where를 사용하여 Stream에서 짝수만 읽는 예제입니다.
void main() {
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5, 6]);
stream.where((data) => data % 2 == 0).listen((data) {
print('짝수: $data');
});
}
Output:
짝수: 2
짝수: 4
짝수: 6
firstWhere()
firstWhere()
는 where()
와 동일한데, 차이점은 조건에 맞는 데이터를 1개만 읽습니다.
Stream.firstWhere(func).then()
처럼 사용할 수 있으며, 가장 먼저 조건에 일치하는 데이터가 then()
으로 전달됩니다.
아래와 같이 Stream의 첫번째 짝수를 읽을 수 있습니다.
void main() {
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5, 6]);
stream.firstWhere((data) => data % 2 == 0).then((data) {
print('첫번째 짝수: $data');
});
}
Output:
첫번째 짝수: 2
4. map() : 이벤트 변환
Stream.map()
은 Stream의 데이터를 map() 함수의 리턴 값으로 변환합니다.
아래 예제는 Stream의 데이터에 2를 곱하여 출력하는 예제입니다. map을 통과하면서 변환된 데이터는 listen()
으로 전달됩니다.
void main() {
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
stream.map((data) => data * 2).listen((data) {
print('data: $data');
});
}
Output:
data: 2
data: 4
data: 6
data: 8
data: 10
asyncMap()
asyncMap()
은 비동기적으로 데이터를 변환합니다. map()
에 async가 추가되었다고 보면 됩니다.
아래와 같이 Stream 이벤트를 asyncMap()
에서 읽을 때 비동기적인 방식으로 처리하여 listen()
에 전달할 수 있습니다.
void main() {
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
stream.asyncMap((data) => Future.delayed(Duration(seconds: 1), () => data * 2)).listen((data) {
print('data: $data');
});
}
Output:
data: 2
data: 4
data: 6
data: 8
data: 10
5. take() : 제한된 수만큼 이벤트 읽기
Stream.take(count)
는 Stream에서 count 개수만큼만 데이터를 읽습니다.
아래 예제는 Stream에서 순차적으로 3개의 이벤트만 읽습니다.
void main() {
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
stream.take(3).listen((data) {
print('data: $data');
});
}
Output:
data: 1
data: 2
data: 3
6. reduce() : 이벤트 누적
reduce()
는 Stream의 데이터를 순차적으로 처리하여 결과를 누적할 때 사용합니다.
Stream.reduce((acc, data) => acc + data)
처럼 사용할 수 있으며, acc + data
의 결과는 다음 이벤트의 acc로 전달됩니다.
아래 예제에서 처음 acc의 초기 값은 0이며, 아래와 같은 순서로 계산되면서 15가 리턴됩니다.
- (0, 1) => 0 + 1
- (1, 2) => 1 + 2
- (3, 3) => 3 + 3
- (6, 4) => 6 + 4
- (10, 5) => 10 + 5
- 15 리턴
void main() {
final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);
stream.reduce((acc, data) => acc + data).then((result) {
print('Result: $result');
});
}
Output:
Result: 15
7. 에러 처리
Stream에서 에러가 발생할 수 있으며, 아래 예제처럼 Stream.handleError()
으로 처리할 수 있습니다.
void main() {
final stream = Stream<int>.error(Exception('에러 발생'));
stream.handleError((error) {
print('Error: $error');
}).listen((data) {
// 처음부터 에러가 발생하여 이 부분은 실행되지 않음
});
}
Output:
Error: Exception: 에러 발생
Related Posts
- Flutter/Dart - Future.delayed() 사용 방법
- Flutter/Dart - Stream 사용 방법
- Flutter/Dart - 파일, 디렉토리 삭제
- Flutter/Dart - 텍스트 파일 쓰기
- Flutter/Dart - 반복문 (for, while, for-in, forEach)
- Flutter/Dart - Static 변수, 메소드 선언
- Flutter/Dart - 텍스트 파일 읽기
- Flutter/Dart - 다양한 Null 체크 방법
- Flutter/Dart - Double을 Int로 변환
- Flutter/Dart - Double을 String으로 변환
- Flutter/Dart - String을 Double로 변환
- Flutter/Dart - String을 List로 변환
- Flutter/Dart - String에서 특정 문자열 Index 찾기
- Flutter/Dart - String 길이(length)
- Flutter/Dart - 몇 초 지연시키기, sleep
- Flutter/Dart - 날짜 계산, 년/월/일 더하고 빼기
- Flutter/Dart - 날짜 문자열을 DateTime으로 변환
- Flutter/Dart - 현재 시간 가져오기, DateTime
- Flutter/Dart - Map에 key-value 추가
- Flutter/Dart - Map 순회 방법
- Flutter/Dart - Map 선언 및 초기화
- Flutter/Dart - Map에서 Entry(key-value) 제거
- Flutter/Dart - key, value로 Map 정렬
- Flutter/Dart - 리스트 복사 방법
- Flutter/Dart - 리스트 합치는 방법
- Flutter/Dart - 리스트 최대값, 최소값 찾기
- Flutter/Dart - 리스트 요소 추가, 특정 위치 추가
- Flutter/Dart - 리스트 요소 제거
- Flutter/Dart - 리스트 합계, 평균 계산
- Flutter/Dart - 리스트 요소 값 변경, 찾기
- Flutter/Dart - 리스트 중복 제거
- Flutter/Dart - 리스트 정렬 (오름차순, 내림차순)
- Flutter/Dart - 리스트 자르기, 분리하기
- Flutter/Dart - 변수 타입 체크
- Flutter/Dart - 리스트 필터링