Dart ရဲ့ Stream ဆိုတာဘာလဲ?

Stream ဆိုတာကို reactive paradigm မှာ အသုံးပြုကြပါတယ်။ data အပြောင်းအလဲပေါ်မူတည်ပြီး သက်ဆိုင်ရာနေရာတွေမှာ အလိုအလျောက် ပြောင်းလဲသွားနိုင်ဖို့ တည်ဆောက်တဲ့အခါ အဓိက အသုံးပြုကြတဲ့ architecutre ပဲ ဖြစ်ပါတယ်။ Bloc pattern ရဲ့ အခြေခံကလဲ သူပဲ ဖြစ်တာကြောင့် state management တွေကို နားလည်ဖို့ ကြိုးစားတဲ့အခါ streams ကိုလဲ နားလည်ထားဖို့ လိုအပ်ပါတယ်။ နာမည်က streams ဆိုတဲ့အတိုင်းပဲ ရေစီးနေတဲ့ ပိုက်ချောင်း တခုအနေနဲ့ မြင်ပြီးတော့ အထဲမှာ စီးနေတဲ့ ရေ တွေက data အနေနဲ့ တွေးကြည့်လို့ရပါတယ်။

💡
A stream is a sequence of asynchronous data.

Official documentation ကို ဖတ်ချင်ရင်တော့ ဒီမှာ ဖတ်လို့ရပါတယ်။

Dart မှာ stream တခုကို တည်ဆောက်ချင်တဲ့အခါ async* ဆိုတဲ့ generator function နဲ့ တည်ဆောက်ပါတယ်။

Stream<int> countStream(int to) async* {
  for (int i = 1; i < to; i++) {
    yield i;
  }
}

အပေါ်မှာ ပြထားတဲ့ stream function ရဲ့ အလုပ်လုပ်ပုံက ၁ကနေ ထည့်ပေးတဲ့ value မရောက်မချင်း value တွေကို တိုးပြီးတော့ return ပြန်ပေးဖို့ ပြောထားတာ ဖြစ်ပါတယ်။ ပုံမှန် function တွေလိုမျိုး result ကို တစ်ခေါက်ပဲ ပြန်ပေးတာမျိုးမဟုတ်ဘဲ yield ဆိုပြီး ပြောထားတဲ့နေရာရောက်တိုင်း ပြန်ပေးနေမှာ ဖြစ်ပါတယ်။ ဒီနေရာမှာ countStream(10) ဆိုပြီး ခေါ်လိုက်တယ်ဆို 1 ကနေ 9 အထိ အဆင့်တိုင်းမှာ value ပြောင်းသွားတာကို ပြန်ပေးသွားမှာ ဖြစ်ပါတယ်။ အစမှာ ပေးခဲ့တဲ့ pipe ဥပမာနဲ့ ကြည့်မယ်ဆို အခုလိုမျိုး stream အစကနေ အဆုံးအထိ value တွေကို return အဆင့်ဆင့် ပြန်ပေးသွားတာကို တွေ့ရမှာ ဖြစ်ပါတယ်။

value တွေကို တိုးပြီးတော့ အခါခါ ပြန်ပေးတာတော့ ဟုတ်ပါပြီ။ အခုလိုပြန်ပေးတဲ့ value ကို normal function တွေမှာ ဘယ်လိုအသုံးပြုကြမလဲ? ဒီနေရာမှာ Future ဆိုတာကို အသုံးပြုဖို့ လိုလာပါပြီ။ Future ဆိုတာကတော့ function ကို invoke လုပ်တဲ့အခါ ချက်ချင်းမဟုတ်ဘဲ တချိန်ချိန်မှာ result ကို ပြန်ပေးဖို့ လိုအပ်တဲ့ နေရာတွေမှာ အသုံးပြုပါတယ်။ သူ့မှာလဲ async ဆိုပြီး သုံးပါတယ်။ async နဲ့ async* မတူတာကို သတိပြုမိမှာပါ။ stream မပြီးမချင်း generator function က value ပြန်တိုင်းမှာ future function ကနေ ရလာတဲ့ value ကိုယူပြီး လုပ်စရာရှိတာတွေ လုပ်သွားလို့ရပါတယ်။ အခုတော့ ကျွန်တော်တို့ ရလာတဲ့ value တွေပေါင်းပြီး စုစုပေါင်း တန်ဖိုးကို ပြန်ပေးဖို့ လုပ်ကြည့်ရအောင်ပါ။

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

ဒီ function ထဲမှာ သုံးထားတဲ့ await က stream ကနေ ရလာတဲ့ value တွေကို for loop မှာ အသုံးပြုမယ့် Iterable ဖြစ်အောင် လုပ်သွားပေးတာပဲ ဖြစ်ပါတယ်။

အပေါ်မှာ ရှင်းပြထားတာတွေ အားလုံးကို အခုလို ချိတ်ဆက်ပြီး အသုံးပြုသွားလို့ရပါပြီ။

void main() async {
  var stream = countStream(10);
  var sum = await sumStream(stream);
  print(sum); // 45
}

ဒီမှာ သုံးထားတဲ့ await ကတော့ Future function ဖြစ်တဲ့ sumStream က result ကို စောင့်ဖို့ သုံးထားတာပဲ ဖြစ်ပါတယ်။