기존 호출 플로우
void main(){ addNumbers(1,1); addNumbers(2,2); //무조건 처음 호출된 함수가 끝난 후에 다음 함수가 실행됨 } void addNumbers(int num1,int num2){ print('calculating.... $num1 + $num2'); print('calculated ! ${num1 + num2}'); }
Future
void main(){ // Future - 미래 (비동기 프로그래밍) // 미래에 받아올 값 // Future를 사용하면 cpu가 기다리지 않고 다른 작업을 할 수 있게 처리할 수 있음(낭비를 줄임) Future<String> name = Future.value('Dylan'); Future<int> number = Future.value(1); Future<bool> isTrue = Future.value(true); addNumbers(1,1); addNumbers(2,2); } void addNumbers(int number1, int number2){ /* * delayed - 2개의 파라미터를 받음 * 1번 param- 지연할 기간 * 2번 param- 콜백 함수 */ print('계산 시작 : $number1 + $number2'); //서버 시뮬레이션 Future.delayed(Duration(seconds:2), (){ print('계산 완료: $number1 + $number2 = ${number1 + number2}'); }); print('함수 완료'); // 계산 완료보다 먼저 실행됨 /* * 계산 시작 : 1 + 1 함수 완료 계산 시작 : 2 + 2 함수 완료 계산 완료: 1 + 1 = 2 계산 완료: 2 + 2 = 4 * */ }
await
await를 쓴다고해서 cpu가 아무것도 안하고 기다리지 않고, 다른 태스크들을 처리하고 데이터가 패칭되었을때 다시 다음 줄을 실행한다. (비동기)
void main(){ Future<String> name = Future.value('Dylan'); Future<int> number = Future.value(1); Future<bool> isTrue = Future.value(true); addNumbers(1,1); addNumbers(2,2); } /* * 순서가 중요할 경우는 async - await를 쓴다. * */ void addNumbers(int number1, int number2) async { print('계산 시작 : $number1 + $number2'); //서버 시뮬레이션 await Future.delayed(Duration(seconds:2), (){ print('계산 완료: $number1 + $number2 = ${number1 + number2}'); }); // 데이터를 패칭할때까지 기다림 print('함수 완료 $number1 + $number2 '); /* 계산 시작 : 1 + 1 계산 시작 : 2 + 2 계산 완료: 1 + 1 = 2 함수 완료 1 + 1 계산 완료: 2 + 2 = 4 함수 완료 2 + 2 */ }
main() 쓰레드에서도 실행순서가 중요할경우 이렇게 쓸 수 있음.
void main() async { Future<String> name = Future.value('Dylan'); Future<int> number = Future.value(1); Future<bool> isTrue = Future.value(true); await addNumbers(1,1); //await는 리턴값이 Future여야 사용가능 await addNumbers(2,2); /* * 계산 시작 : 1 + 1 계산 완료: 1 + 1 = 2 함수 완료 1 + 1 계산 시작 : 2 + 2 계산 완료: 2 + 2 = 4 함수 완료 2 + 2 * */ } Future<void> addNumbers(int number1, int number2) async { // return 타입을 Future<void>로 변경 print('계산 시작 : $number1 + $number2'); //서버 시뮬레이션 await Future.delayed(Duration(seconds:2), (){ print('계산 완료: $number1 + $number2 = ${number1 + number2}'); }); print('함수 완료 $number1 + $number2 '); }
async 함수 비동기 return
void main() async { Future<String> name = Future.value('Dylan'); Future<int> number = Future.value(1); Future<bool> isTrue = Future.value(true); final result = await addNumbers(1,1); //await는 리턴값이 Future여야 사용가능 final result2 = await addNumbers(2,2); print('result1 : $result'); print('result2: $result2'); print('result1 + result2 = ${result + result2}'); } Future<int> addNumbers(int number1, int number2) async { // return 타입을 Future<void>로 변경 print('계산 시작 : $number1 + $number2'); //서버 시뮬레이션 await Future.delayed(Duration(seconds:2), (){ print('계산 완료: $number1 + $number2 = ${number1 + number2}'); }); print('함수 완료 $number1 + $number2 '); return number1 + number2; }
Stream
Future는 await 를 이용하여 값이 다 불러올때까지 기다리지만, Stream은 완료되는 순간까지 지속적으로 값을 불러올 수 있다.
import 'dart:async'; void main() { final controller = StreamController(); // final stream = controller.stream; // 한개만 listen final stream = controller.stream.asBroadcastStream(); // 여러번 listening 할 수 있는 stream 생성 // stream에 값이 들어왔을때 콜백함수 실행 final streamListener1 = stream.where((val) => val % 2 == 0).listen((val) { print('Listener 1 : $val'); }); final streamListener2 = stream.where((val) => val % 2 == 1).listen((val) { print('Listener 2 : $val'); }); controller.sink.add(1); // data input controller.sink.add(2); controller.sink.add(3); controller.sink.add(4); controller.sink.add(5); /* * Listener 2 : 1 Listener 1 : 2 Listener 2 : 3 Listener 1 : 4 Listener 2 : 5 * */ }
함수로 stream 제공
import 'dart:async'; void main() { calculate(1).listen((val){ print('calculate(1) : $val'); }); /* * 결과값 * calculate(1) : 0 calculate(1) : 1 calculate(1) : 2 calculate(1) : 3 calculate(1) : 4 * */ } // calculate(int number) { // for (int i = 0; i < 5; i++) { // return i * number; // return을 하면 함수가 끝나버리기때문에, 맨 처음값만 돌려줄수있음 // } // } // Stream return type을 사용하여 listen 하고있는 곳에 값을 뿌려줄 수있음. Stream<int> calculate(int number) async*{ for (int i = 0; i < 5; i++) { yield i * number; // yield 는 값을 하나하나 보내줄때 사용됨 } }
리턴 타입이 Stream 일때도 await 사용가능
import 'dart:async'; void main() { calculate(1).listen((val){ print('calculate(1) : $val'); }); } Stream<int> calculate(int number) async*{ for (int i = 0; i < 5; i++) { yield i * number; // yield 는 값을 하나하나 보내줄때 사용됨 await Future.delayed(Duration(seconds:1)); //async* 여도 await를 쓸 수 있음, 1초마다 데이터 전송 } }
비동기 증명
import 'dart:async'; void main() { calculate(2).listen((val){ print('calculate(2) : $val'); }); calculate(4).listen((val){ print('calculate(4) : $val'); }); /* * 끝날때까지 기다리지않고 calculate(4)가 실행됨 >> 비동기 * calculate(2) : 0 calculate(4) : 0 calculate(2) : 2 calculate(4) : 4 calculate(2) : 4 calculate(4) : 8 calculate(2) : 6 calculate(4) : 12 calculate(2) : 8 calculate(4) : 16 * */ } Stream<int> calculate(int number) async*{ for (int i = 0; i < 5; i++) { yield i * number; // return을 하면 함수가 끝나버리기때문에, 맨 처음값만 돌려줄수있음 await Future.delayed(Duration(seconds:1)); //async* 여도 await를 쓸 수 있음, 1초마다 데이터 전송 } }
만약 위에서 calculate(2)가 전부 실행되고 난다음, calculate(4)가 실행되게 하려면 어떻게 해야할까? (Future의 await 기능)
새로운 순서를 조정해주는 함수를 만든 후, yield* 를 사용하여 실행 순서를 보장한다.
import 'dart:async'; void main() async { playAllStream().listen((val){ print(val); }); /* 결과값 * 0 1 2 3 4 0 1000 2000 3000 4000 * */ } Stream<int> playAllStream() async*{ yield* calculate(1); // yield* 은 stream에 모든 값을 다 가져올때까지 기다림 (Future의 await와 비슷함) yield* calculate(1000); } Stream<int> calculate(int number) async*{ for (int i = 0; i < 5; i++) { yield i * number; // yield 는 값을 하나하나 보내줄때 사용됨 await Future.delayed(Duration(seconds:1)); //async* 여도 await를 쓸 수 있음, 1초마다 데이터 전송 } }