동기 프로그래밍과 비동기 프로그래밍 동기 프로그래밍함수를 실행하면 다음 코드가 실행되기 전에 해당 함수의 결과 값이 먼저 반환 예시요청1 → 응답1 → 요청2 → 응답2 → 요청3 → 응답3 비동기 프로그래밍요청한 결과를 기다리지 않으며 응답 순서 또한 요청한 순서와 다를 수 있다. 예시요청1 → 요청2 → 응답1 → 응답2 → 요청3 → 응답3 Future 시간이 오래걸리는 작업을 기다린 후 결과값을 받아와야하는 비동기 프로그래밍을 위해 만들어진 제네릭 클래스 미래 어느 시점에서 사용할 수 있는 잠재적인 값 또는 에러 최종 결과 값을 반환한다. void main () {
futureExample ();
}
void futureExample (){
print ( "메소드 시작!!!" );
Future . delayed ( const Duration ( seconds: 3 ), (){
print ( "비동기 실행!!!" );
});
print ( "메소드 종료!!!" );
}
출력 결과
메소드 시작!!! 메소드 종료!!! 비동기 실행!!!
async와 await async await 처리한 비동기 메소드를 실행하기 위해 필요한 키워드 비동기 메소드가 실행될 메소드의 파라미터와 몸체 사이에 선언한다. 내부에 await 키워드가 존재하지 않아도 사용할 수 있다. 해당 키워드가 있어야지 Future를 반환형으로 사용할 수 있다. await 비동기 메소드를 동기 처리 하기 위한 키워드 동기 처리할 비동기 메소드 앞에 선언한다. Future 타입에만 사용할 수 있다. Future 타입의 값을 T 타입으로 반환한다. 예시 (async와 await) void main () async {
await futureExample ();
var result = await futureExample2 ();
print ( result . runtimeType );
}
Future < void > futureExample () async {
print ( "메소드 시작!!!" );
await Future . delayed ( const Duration ( seconds: 3 ), (){
print ( "비동기 실행!!!" );
});
print ( "메소드 종료!!!" );
}
Future < int > futureExample2 () async {
return 3 ;
}
출력 결과
메소드 시작!!! 비동기 실행!!! 메소드 종료!!! int
Future 관련 메소드 then Future 객체를 반환하는 메소드의 결과에 대해서 처리하는 메소드 Future 객체의 제네럴 타입이 반환되는 값의 자료형이 된다. void main () {
futureExample () . then (( value ){
print ( value . runtimeType ); //출력 : int
});
}
Future < int > futureExample () async {
return 7 ;
}
Future.delayed 지연 시간을 생성할 때 사용한다. 지연 시간이 완료될 후 실행될 함수를 인자로 넣을 수 있다. void main () {
Future . delayed ( const Duration ( seconds: 3 ), () {
print ( "3초 지남!!!" );
},);
}
Future.any Future 메소드들을 배열로 받는다. 가장 먼저 완료된 Future의 결과만 반환한다. Future.any의 결과와 별개로 내부의 Future 메소드들은 멈추지 않고 계속 실행된다. void main () {
Future . any ([
Future . delayed ( const Duration ( seconds: 1 ), (){ return 1 ;}),
Future . delayed ( const Duration ( seconds: 2 ), (){ return 2 ;}),
Future . delayed ( const Duration ( seconds: 3 ), (){ return 3 ;}),
]) . then (( value ) {
print ( "반환된 값 : $value " );
},);
}
출력 결과
반환된 값 : 1
Future.wait Future 메소드들을 배열로 받는다. 각 Future들의 결과값을 배열로 반환한다. 선언한 순서와 배열에 저장되는 결과값 요소의 순서는 동일하다. void main () {
Future . wait ([
Future . delayed ( const Duration ( seconds: 1 ), (){ return 1 ;}),
Future . delayed ( const Duration ( seconds: 2 ), (){ return 2 ;}),
Future . delayed ( const Duration ( seconds: 3 ), (){ return 3 ;}),
]) . then (( value ) {
print ( "반환된 값 : $value " );
},);
}
출력 결과
반환된 값 : [1, 2, 3]
Stream 시간이 오래걸리는 작업을 기다린 후 결과값을 받아와야하는 비동기 프로그래밍을 위해 만들어진 제네릭 클래스 미래 어느 시점에서 사용할 수 있는 잠재적인 값 또는 에러 데이터나 이벤트가 들어오는 통로 한 개 이상의 Future들의 조합 메소드 처리 과정 중간중간에 여러 번의 반응이 가능하다. 종류단일 구독 스트림 (Single Subscription Streams)stream에 최대 1개의 listener를 생성할 수 있다. 방송 스트림 (Broadcast Streams)stream에 여러 개의 listener를 생성할 수 있다. 단일 구독 스트림 하나의 stream에 최대 1개의 listener를 생성할 수 있다. 하나의 stream에 2개 이상의 listener 생성 시 오류가 발생한다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream ;
//listener 생성
final streamListener = stream . listen (( value ){
print ( "listen value : $value " );
});
//값 전달
controller . sink . add ( 1 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
controller . sink . add ( 4 );
controller . sink . add ([ 5 , 6 , 7 ]);
controller . sink . add (() = >[ 8 , 9 , 10 ]);
}
출력 결과
listen value : 1 listen value : 2 listen value : 3 listen value : 4 listen value : [5, 6, 7] listen value : Closure ‘main_closure0’
방송 스트림 한 번에 하나씩 처리할 수 있는 개별 메시지를 위한 것 stream에서 asBroadcastStream() 메소드를 통해 얻어낸다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너1 생성
final streamListener1 = stream . listen (( value ){
print ( "listener1 : $value " );
});
//리스너2 생성
final streamListener2 = stream . listen (( value ){
print ( "listener2 : $value " );
});
//값 전달
controller . sink . add ( 1 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
}
출력 결과
listener1 : 1 listener2 : 1 listener1 : 2 listener2 : 2 listener1 : 3 listener2 : 3
Stream 관련 메소드 조건 추가하기 where 메소드를 통해 리스너가 받을 수 있는 값에 제한을 둘 수 있다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너1 생성, 홀수만 출력
final streamListener1 = stream . where (( value ) = > value % 2 == 0 ) . listen (( value ){
print ( "listener1 : $value " );
});
//리스너2 생성, 짝수만 출력
final streamListener2 = stream . where (( value ) = > value % 2 == 1 ) . listen (( value ){
print ( "listener2 : $value " );
});
//값 전달
controller . sink . add ( 1 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
}
출력 결과
listener2 : 1 listener1 : 2 listener2 : 3
지정한 개수만 받기 take 메소드를 통해 지정한 횟수만큼만 값을 받을 수 있게할 수 있다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너 생성
final streamListener = stream . take ( 3 ) . listen (( value ){
print ( "listener : $value " );
});
//값 전달
controller . sink . add ( 1 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
controller . sink . add ( 4 );
controller . sink . add ( 5 );
}
출력 결과
listener : 1 listener : 2 listener : 3
스킵하기 skip 메소드를 통해 지정한 횟수만큼 스킵할 수 있다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너 생성
final streamListener = stream . skip ( 3 ) . listen (( value ){
print ( "listener : $value " );
});
//값 전달
controller . sink . add ( 1 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
controller . sink . add ( 4 );
controller . sink . add ( 5 );
}
출력 결과
listener : 4 listener : 5
조건을 만족하는 값만 받기 takeWhile 메소드를 통해 지정한 조건에 맞는 값만 받을 수 있다. 지정한 조건에 대해서 한 번이라도 false가 나오는 경우에는 반복문이 종료된다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너 생성
final streamListener = stream . takeWhile (( element ) = > element % 2 == 0 ) . listen (( value ){
print ( "listener : $value " );
});
//값 전달
controller . sink . add ( 0 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
controller . sink . add ( 4 );
controller . sink . add ( 5 );
}
출력 결과
listener : 0 listener : 2
조건을 만족하는 값 스킵하기 skipWhile 메소드를 통해 지정한 조건에 맞는 값은 스킵할 수 있다. takeWhile과 달리 지정한 조건에 대해서 한 번이라도 false가 나오는 경우에도 반복문이 종료되지는 않는다. 지정한 조건에 대해서 false가 나오면 그 값부터 마지막 값까지 스킵없이 전부 받아들이게 된다. import 'dart:async' ;
void main () {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너 생성
final streamListener = stream . skipWhile (( element ) = > element % 2 == 0 ) . listen (( value ){
print ( "listener : $value " );
});
//값 전달
controller . sink . add ( 0 );
controller . sink . add ( 2 );
controller . sink . add ( 3 );
controller . sink . add ( 4 );
controller . sink . add ( 5 );
}
시간 제한 추가하기 timeout를 통해서 값을 받아들이기까지의 시간 제한을 추가한다. 리스너가 지정한 시간 안에 데이터를 받지 못하면 에러를 발생시킨다.에러가 발생하기 하지만 리스너 자체가 종료되지는 않는다. import 'dart:async' ;
void main () async {
final controller = StreamController ();
final stream = controller . stream . asBroadcastStream ();
//리스너 생성
final streamListener = stream . timeout ( const Duration ( seconds: 1 ) , onTimeout: ( sink ) {
print ( "timeout 발생 / $sink " );
},) . listen (( value ){
print ( "listener : $value " );
});
//값 전달
controller . sink . add ( 0 );
await Future . delayed ( const Duration ( seconds: 2 ));
controller . sink . add ( 2 );
await Future . delayed ( const Duration ( seconds: 2 ));
controller . sink . add ( 3 );
await Future . delayed ( const Duration ( seconds: 2 ));
controller . sink . add ( 4 );
await Future . delayed ( const Duration ( seconds: 2 ));
controller . sink . add ( 5 );
}
출력 결과
listener : 0 timeout 발생 / Instance of ‘_ControllerEventSinkWrapper' listener : 2 timeout 발생 / Instance of '_ControllerEventSinkWrapper' listener : 3 timeout 발생 / Instance of '_ControllerEventSinkWrapper' listener : 4 timeout 발생 / Instance of '_ControllerEventSinkWrapper' listener : 5 timeout 발생 / Instance of '_ControllerEventSinkWrapper'
Stream.periodic 지정한 시간마다 한 번씩 실행되는 스트림을 생성한다. take 메소드가 없으면 종료되지 않고 계속 반복된다. void main () {
Stream . periodic ( const Duration ( seconds: 1 )) . take ( 5 ) . listen (( value ){
print ( "listener : $value " );
});
}
Stream.fromIterable 지정한 컬렉션을 통해서Fu 스트림을 생성한다. void main () {
Stream . fromIterable ([ 1 , 2 , 3 , 4 , 5 ]) . listen (( value ){
print ( "listener : $value " );
});
}
Stream.fromFuture 지정한 Future 객체에서 스트림을 생성한다. void main () {
Stream . fromFuture ( Future ((){
return [ 1 , 2 , 3 ];
})) . listen (( value ){
print ( "listener : $value " );
});
}
async*와 yield async* Stream 객체를 반환하게 하는 키워드 메소드의 파라미터 부분과 몸체 부분 사이에 선언한다. yield stream에 listener에 지속적으로 값을 반환해주는 키워드 일반적인 메소드에서 값을 반환하는 경우의 return 대신에 사용한다. 예시 (async*와 yield) void main () {
streamExample () . listen (( value ) {
print ( "listener : $value " );
},);
}
Stream < int > streamExample () async * {
for ( int i = 0 ; i < 5 ; i ++ ){
yield i ;
}
}
출력 결과
listener : 0 listener : 1 listener : 2 listener : 3 listener : 4
yield* Stream을 통한 재귀 생성 함수 사용하는 키워드 예시 (yield*) int num = 5 ;
void main () {
streamExample () . listen (( value ) {
print ( "listener : $value " );
},);
}
Stream < int > streamExample () async * {
yield num -- ;
if ( num > 0 ){
yield * streamExample ();
}
}
Isolate Isolate란? 싱글스레드 언어인 Dart를 지원하기 위한 비동기 프로그래밍 기법 다른 언어의 스레드 프로그래밍같은 역할을 한다. Isolate의 특징 Isolate는 스레드와 다르게 메모리를 공유하지 않는다. 각 Isolate마다 고유의 메모리 공간을 가진다. 각 Isolate 내부에 event loop가 있어서 이벤트가 들어올때 마다 이를 처리한다. 하나의 작업을 여러개의 Isolate로 처리하고 싶을때 메세지를 이용하에 Isolate간 통신을 할 수 있다. 기본 형식 import 'dart:isolate' ;
void main () async {
Isolate ? isolate ;
isolate = await Isolate . spawn < String >(( message ) {
//실행할 내용
}, '전달할 메시지' );
}