
اول از همه اگر با استریم ها و بلاک توی دارت و فلاتر آشنا نیستی بدی نیست که بری دربارش بخونی
دوم اینکه هدف من از این نوشته، نوشتن bloc هست که با تاخیر اون Event که ارسال میشه اجرا بشه که این تاخیر بخاطر وارد کردن کلمه توسط کاربر هست
سوم اینکه مثالی که میزنم فقط bloc رو پوشش میده و فقط مفاهیم رو توضیح میدم
بطور خلاصه و خیلی سریع بخوام بگم، RxDart یه کتابخونه ایه که قابلیت برنامه نویسی واکنش گرا رو بهمون میده یه یکم ساده تر میاد روی استریم ها یسری متود های اضافه بهمون میده، لیستشو میتونی اینجا ببینی که زیاد هم هست و ما در این نوشته از چهارتا متود اون استفاده میکنیم که شامل
debounceTime
flatMap
switchMap
MergeStream
debounceTime همینطو که از اسمش پیداست میاد تاخیر زماندار میندازه یعنی میاد emit روی یک استریم رو بعد از زمان مشخصی اعمال میکنه.
flatMap میاد هر emit روی استریم رو بصورت پشت سر هم اجرا میکنه یعنی تاخیری چیزی نداریم و اگر emit جدید بیاد هم اجرا میشه
switchMap هم میاد هر emit روی استریم که انجام میشه اگر هنوز کامل نشده پروسه emit شدن و یک emit جدید اومد اون قبلی رو متوقف میکنه و جدید رو میبره به مرحله emit
MergeStream هم از اسمش پیداست میاد استریم هارو باهم ادغام میکنه و یک استریم به ما تحویل میده
به کد زیر توجه کنید
class MyBlocEvents {} class MyBlocSearch extends MyBlocEvents { final String query; MyBlocSearch({this.query = ""}); } class MyBlocOtherEvent extends MyBlocEvents { final String extraData; MyBlocNew({this.extraData = ""}); } class MyBloc extends Bloc<MyBlocEvents, int> { MyBloc(super.initialState) { on<MyBlocEvents>( (event, emit) async { if (event is MyBlocSearch) { print("Search Event"); } else { print("Other Event"); } }, transformer: (eventStream, mapperEventFunction) { final debouncedTimeStream = eventStream .where((event) => event is MyBlocSearch) .debounceTime(Duration(seconds: 3)); final normalStream = eventStream.where((event) => event is! MyBlocSearch); return MergeStream([ debouncedTimeStream.switchMap(mapperEventFunction), normalStream.flatMap(mapperEventFunction), ]); }, ); } }
خب توی کد بالا ما یه event داریم که MyBlocEvent هست و دو ایونت دیگه که ارث بری شده از MyBlocEvent هست به نام های MyBlocSearch و MyBlocOtherEvent
یه bloc نوشتم که ایونت های MyBlocEvent رو قبول میکنه و مقدار emit اون عدد هست (int)
توی بلاک اگر ایونت MyBlocSearch بود پرینت میکنه "Search Event" وگر نه پرینت میکنه "Other Event"
برای اینکه بتونیم کاستومایز کنیم ایونت هامون رو میایم transformer رو کاستومایز میکنیم به این شکل که transformer ما یه فانکشن میگره که آرگیومنت های eventStram و mapperEventFunction هست
eventStram استریمی از ایونت های ما
mapperEventFunction فانکشنی که آرگیومنتش ایونت ما و خروجریش استریمی از ایونت ماست (که خیلی مهم نیست زیاد خودتو درگیر نکن)
خروجی این transformer ما باید استریمی از نوع ایونت ما باشد
خب میام اول چک میکنم که آیا ایونتی که پاس داده شده از نوع MyBlocSearch هست یا نه که اگر بود میگم که یه تاخیر سه ثانیه ای بزار با استفاده از debounceTime که این میشه اولین استریم تاخیر دار ما
final debouncedTimeStream = eventStream .where((event) => event is MyBlocSearch) .debounceTime(Duration(seconds: 3));
و میگم اگر از نوع MyBlocSearch نبود از نوع استریم نرماله و نیازی به تاخیر نیست
final normalStream = eventStream.where((event) => event is! MyBlocSearch);
حالا نیازه که یه ترکیب بزنیم که هردو نوع زمانی که Event اومد اجرا بشه که از MergeStream استفاده میکنیم و اینو بگم هنوز پایان کار نیست و باید نحوه اجرا شدن هر استریم رو هم بگیم
return MergeStream([ debouncedTimeStream.switchMap(mapperEventFunction), normalStream.flatMap(mapperEventFunction), ]);
اینجا گفتم debouncedTimeStream رو به روش switchMap اجرا کن و اون فانکشن رو هم بهش دادم، حالا این روش چیکار میکنه، فرض کن که ایونتی ارسال کردی که از نوع MyBlocSearch هست خب میاد سه ثانیه صبر میکنه اگر قبل از سه ثانیه دوباره این ایونت رو ارسال کردی ایونت اولیت رو متوقف میکنه و این جدیده میاد رو کار حالا این برای کی خوبه زمانی که کاربر داره تایپ میکنه مثلا "عدنان کمالی" و bloc میخواد از سرور نتیجه رو بگیره با این حرکت که زدیم بعد از اینکه "ع" رو وارد کرد سه ثانیه منتظر میمونه اگر بعد از سه ثانیه حرفی وارد نکرد خب میره همین "ع" رو برات از سرور میاره و اگه ادامه داد مثلا شد "عد" قبل از سه ثانیه ایونت "ع" متوقف و ایونت "عد" شروع میشه و الی آخر.
normalStram هم به روش flatMap که همون روش عادی هست.
final myBloc = MyBloc(0); myBloc.add(MyBlocSearch(query: "ع")); await Future.delayed(Duration(seconds: 2)); myBloc.add(MyBlocSearch(query: "عد")); await Future.delayed(Duration(seconds: 4)); myBloc.add(MyBlocSearch(query: "عدن")); myBloc.add(MyBlocOtherEvent(extraData: "Extra Data"));
توی این کد من بلاکم رو ساختم به اسم myBloc
بهش ایونت MyBlocSearch زدم و بعد از دو ثانیه دوباره یه ایونت MyBlocSearch زدم، بعد از چهار ثانیه دوباره ایونت MyBlocSearch رو زدم و در آخر هم یه ایونت MyBlocOtherEvent زدم و خروجی شد
Search Event // بعد از پنج ثانیه Other Event Search Event // بعد از نه ثایه
اولین خروجی بعد از یه دو ثانیه و سه ثانیه که برای تاخیر توی بلاک بود یعنی بعد از پنج ثانیه اجرا شد و کوئری "عد" بود چون بعدش چهار ثانیه delay داده بودیم و کوئری "ع" به دلیل کمتر از سه ثانیه بودن (چون ما دوثانیه delay داده بودیم متوقف شد)، بعدش MyBlocOtherEvent چون هیچ تاخیری نداشت اجرا شد و ایونت MyBlocSearch که کوئری "عدن" داشت توی تاخیر سه ثانیه بود و بعد از MyBlocOtherEvent اجرا شد
امیدوارم که این نوشته براتون مفید بوده باشه
منبع من برای این نوشته: