Dart 의 비동기/동기(async/ sync programming) 표현 방법은 상당히 직관적이면서도 편리한 부분들이 많습니다.
효율적인 동작을 위한 코딩을 하려다 보면 필연적으로 async로 코드 작성이 필요로 하게 되죠.
아래 간단한 주제를 가지고 한번 얘기 해보고자 합니다.
A라는 데이타 목록을 가지고 B라는 과정을 거쳐서 C라는 결과를 도출하고 D라는 데이타 목록으로 저장을 하려고 합니다. 1. A의 각 항목들은 B라는 과정을 거칠때 1~2 초 정도 시간이 걸린다. 2. B 과정을 입력 A 데이타에 따라 처리 시간이 달라진다.(1초 또는 2초) 3. D에 저장 되는 순서는 A의 순서와 같아야 한다. |
일단 A항목들을 순차적으로 처리하게 되면, A의 개수만큼 시간이 늘어나게 됩니다.
따라서 비동기(async)프로그래밍를 고려하게 될 것입니다.
위의 상황에 대해서 코딩을 해보면, 가장먼저 다음과 같이 생각해볼 수 있을 것입니다.
A = srcData, B =Future.delayed() , C = A와 같은 type , D = dstData 라고 합시다.
import 'dart:async';
import 'dart:math';
List<String> srcData=[];
List<String> dstData=[];
init(){
for( int i = 0;i<24; i++ ){
srcData.add('mmm_${i}');
}
}
Future copyToDst(int i){// test를 위해서 1~2초의 랜덤 수행 순서로, src에서 dstData 에 copy 하는 내용.
return Future.delayed(Duration(seconds: Random().nextInt(2)),(){
dstData.add(srcData[i]);
print('src[$i]=>dst');
});
}
fillDest() async{
init();
print("fillDest: length=${srcData.length}");
for(int s = 0; s<srcData.length ;s++){
copyToDst(s);
}
}
void main() async{
await fillDest();
await Future.delayed(Duration(seconds: 5));
print(dstData);
}
1초 또는 2초에 처리가 완료 되면, dstData에 저장을 하게 됩니다.
실행 결과는 아래와 같습니다.
기본적인 동작은 하는것 처럼 보입니다.
<실행 결과>
fillDest: length=24
src[3]=>dst
src[4]=>dst
src[5]=>dst
src[8]=>dst
src[10]=>dst
src[11]=>dst
src[15]=>dst
src[16]=>dst
src[20]=>dst
src[21]=>dst
src[22]=>dst
src[0]=>dst
src[1]=>dst
src[2]=>dst
src[6]=>dst
src[7]=>dst
src[9]=>dst
src[12]=>dst
src[13]=>dst
src[14]=>dst
src[17]=>dst
src[18]=>dst
src[19]=>dst
src[23]=>dst
[mmm_3, mmm_4, mmm_5, mmm_8, mmm_10, mmm_11, mmm_15, mmm_16, mmm_20, mmm_21, mmm_22, mmm_0, mmm_1, mmm_2, mmm_6, mmm_7, mmm_9, mmm_12, mmm_13, mmm_14, mmm_17, mmm_18, mmm_19, mmm_23]
|
음... 그런데 결과에서 보듯이 "mmm_숫자" 결과의 순서가 srcData의 순서와 맞지 않는 것을 볼수 있습니다.
이제 여기서 몇가지 고민을 하게 되겠죠? 가령 "어떻게 순서를 맞출 것인가? " 와 같은 것 말이죠.
대략 한 3가지 방법을 생각해 볼 수 있을것 같네요..
첫번째, copyToDest를 await 으로 처리하는 방법입니다.
for(int s = 0; s<srcData.length ;s++){
await copyToDst(s);
}
직접 실행해보면, .. 으악... 이런 최악의 수네요..ㅠ_ㅠ 실행시간이 너무 오래걸려요.
두번째, map을 이용하는 방법이 있겠네요.
map을 이용해서 srcData의 순서를 map의 index로 사용하는 방법이죠.
import 'dart:async';
import 'dart:math';
List<String> srcData=[];
Map<int, String> dstData={};
init(){
for( int i = 0;i<24; i++ ){
srcData.add('mmm_${i}');
}
}
Future copyToDst(int i){// test를 위해서 1~2초의 랜덤 수행 순서로, src에서 dstData 에 copy 하는 내용.
return Future.delayed(Duration(seconds: Random().nextInt(2)),(){
dstData[i] = srcData[i];
print('src[$i]=>dst');
});
}
fillDest() async{
init();
print("fillDest: length=${srcData.length}");
for(int s = 0; s<srcData.length ;s++){
copyToDst(s);
}
}
void main() async{
await fillDest();
await Future.delayed(Duration(seconds: 5));
print(dstData);
}
<실행결과>
fillDest: length=24
src[0]=>dst
src[1]=>dst
src[2]=>dst
src[3]=>dst
src[5]=>dst
src[6]=>dst
src[7]=>dst
src[11]=>dst
src[12]=>dst
src[16]=>dst
src[18]=>dst
src[21]=>dst
src[22]=>dst
src[4]=>dst
src[8]=>dst
src[9]=>dst
src[10]=>dst
src[13]=>dst
src[14]=>dst
src[15]=>dst
src[17]=>dst
src[19]=>dst
src[20]=>dst
src[23]=>dst
{0: mmm_0, 1: mmm_1, 2: mmm_2, 3: mmm_3, 5: mmm_5, 6: mmm_6, 7: mmm_7, 11: mmm_11, 12: mmm_12, 16: mmm_16, 18: mmm_18, 21: mmm_21, 22: mmm_22, 4: mmm_4, 8: mmm_8, 9: mmm_9, 10: mmm_10, 13: mmm_13, 14: mmm_14, 15: mmm_15, 17: mmm_17, 19: mmm_19, 20: mmm_20, 23: mmm_23}
|
실행결과를 보시면 데이타의 순서는 처리속도에 따라 순서가 달라졌지만, 우리는 map의 index를 사용할 수 있기 때문에 index 순서로 결과를 뽑아볼수 있습니다. ^^ 매우 좋은 결과네요.
속도도 거의 차이가 없고, 결과도 좋고요.
한가지 걸리는 것은 , dstData를 얻기까지 1~2초가 걸리는데, data가 정상적으로 들어왔는지, 언제 사용해야 하는지가 약간은 애매하네요.
세번째, dstData를 Future List로 변경하는 방법인데요.
dstData 의 형태를 List<String> 에서 List<Future<String>> 변경하고 copyToDst의 형태도 dstData에 Future<String>을 추가하도록 변경되었습니다.
이렇게 하면 dstData 에는 실행 결과가 들어가는 것이 아니라 실행 명령 순서인 Future<String> 을 저장하는 것을 의도 합니다.
import 'dart:async';
import 'dart:math';
List<String> srcData=[];
List<Future<String>> dstData=[];
init(){
for( int i = 0;i<24; i++ ){
srcData.add('mmm_${i}');
}
}
Future copyToDst2(int i)async{// test를 위해서 1~2초의 랜덤 수행 순서로, src에서 dstData 에 copy 하는 내용.
return dstData.add(Future<String>.delayed(Duration(seconds: Random().nextInt(2)),(){
print('src[$i]=>dst');
return srcData[i];
}));
}
fillDest() async{
init();
print("fillDest: length=${srcData.length}");
for(int s = 0; s<srcData.length ;s++){
copyToDst2(s);
}
}
void main() async{
await fillDest();
await Future.delayed(Duration(seconds: 5));
List<String> l=[];
for (var d in dstData){
l.add(await d);
}
print(l);
}
추후 Future<String>의 결과를 얻기 위해서는 await dstData[i] 형태로 사용하면 String을 얻어올 수 있게 됩니다.
<실행 결과>
fillDest: length=24
src[0]=>dst
src[1]=>dst
src[2]=>dst
src[3]=>dst
src[5]=>dst
src[6]=>dst
src[7]=>dst
src[10]=>dst
src[11]=>dst
src[12]=>dst
src[16]=>dst
src[20]=>dst
src[23]=>dst
src[4]=>dst
src[8]=>dst
src[9]=>dst
src[13]=>dst
src[14]=>dst
src[15]=>dst
src[17]=>dst
src[18]=>dst
src[19]=>dst
src[21]=>dst
src[22]=>dst
[mmm_0, mmm_1, mmm_2, mmm_3, mmm_4, mmm_5, mmm_6, mmm_7, mmm_8, mmm_9, mmm_10, mmm_11, mmm_12, mmm_13, mmm_14, mmm_15, mmm_16, mmm_17, mmm_18, mmm_19, mmm_20, mmm_21, mmm_22, mmm_23]
|
추가로,
map을 사용했을때 " dstData 언제 사용해야 하는지가 약간은 애매하네요." 라고 했던 부분에 대해서 추가로 생각해보자면,
List<Future<String>> 을 사용했던것 처럼, map<int , Future<String>> 으로 사용하게 되면, 필요한 데이타를 사용할때 await을 활용하여 data 결과가 채워지기를 기다릴 수 있겠는데요 ?
아무튼 async 프로그래밍을 다루기에 편리한 언어인 dart 인데 이를 활용 안할 이유가 없을것 같죠? 그렇죠?
다들 dart의 async, async* sync, sync* 에 대해서 찾아보시고 알아두시면 즐거운 코딩 생활에 도움이 될것 같습니다.