Dart / العقود: الأنماط والواجهات العامة والنتائج غير المتزامنة / الدرس 12
امتلاك اشتراكات التدفق ودلالات التسليم
عالج القيم والأخطاء والإتمام والإلغاء وسلوك الاشتراك الواحد أو البث، دون افتراض الإعادة أو محدودية التخزين الوسيط.
في هذه الصفحة
أهداف الدرس
- ميّز مصدر Stream من اشتراك نشط ومن عمر الموارد التابعة له.
- اختر معالجة تحافظ على البيانات المطلوبة عندما يبطؤ المستمع أو يغيب.
يحمل التدفق أكثر من القيم الناجحة
قد توصل Stream أحداث بيانات وأخطاء وحدث انتهاء. لا يعني الخطأ عموما استحالة إصدار قيمة أخرى؛ فتحدد سياسة الاشتراك والاستهلاك معالجته. ينتج async* تدفقا، وتستهلكه await for بتدفق تحكم غير متزامن متتابع. أما listen فتعيد StreamSubscription يجب أن يديرها مالك. حدّد هل يملك المصدر ملفا أو مقبسا أو مؤقتا أو لا مورد خارجي. لا يحدد النوع وحده الدوام أو الإعادة أو ترتيب مصادر مستقلة أو مقدار تخزين التنفيذ.
استهلك تدفقا منتهيا بإتمام ظاهر
يصدر المولد أدناه عددين ثابتين، وتحولهما await for وتطبعهما بالترتيب قبل علامة النهاية. لا يوجد مؤقت أو إدخال وإخراج خارجي أو controller أو مشاركة بث. توضح الصيغة البسيطة الاستهلاك المتتابع. إذا انتظر الجسم عملية بطيئة، تتبع معالجة اللاحق سلوك التدفق والاشتراك ولا تنشئ عمالا متوازيين سحريا. لا تضف التوازي دون سياسة ترتيب وخطأ وموارد. البرنامج مصدر كامل لكن مخرجه محلل لغياب بيئة Dart.
import 'dart:async';
Stream<int> counts() async* {
yield 1;
yield 2;
}
Future<void> main() async {
await for (final count in counts()) {
print(count * 10);
}
print('done');
}
10
20
done
المخرج المتوقع 10 ثم 20 ثم done. تنتظر الحلقة إتمام التدفق قبل done. لم يُنفذ تدفق أو مجدول أو اختبار توقيت.
يخدم الاشتراك الواحد والبث حاجتين مختلفتين
يُقصد تدفق الاشتراك الواحد لمستمع واحد، بما يشمل محاولة لاحقة بعد انتهاء الأول. يسمح البث بمستمعين عدة، لكنه لا يعيد الأحداث القديمة تلقائيا لمتأخر. يعتمد سلوك غياب المستمع وتنشيط المورد على المصدر. لذلك ليس التحويل للبث إصلاحا عاما لخطأ تكرار الاستماع؛ فقد يغير الفقد والعمر. حدّد هل يحتاج كل مستهلك كل حدث أو اللاحق فقط أو الحالة الحالية. تحتاج أوامر تعديل دفعات لازمة تصميما أقوى من قناة إشعار عابرة.
عالج خطأ التدفق عند حد استهلاكه
يستخدم المثال Stream.error لإنشاء خطأ اصطناعي واحد ويلتقطه حول await for. لا يستنتج أن كل إخفاق إنتاج FormatException أو أن كل خطأ ينهي التطبيق. ومع listen قدّم معالجة الخطأ والنهاية واحتفظ بالاشتراك عند الحاجة لإلغائه مع العمر. لا تتجاهل الخطأ وتعرض نجاحا كاملا للمستخدم. احفظ سياقا آمنا يميز بيانات مشوهة ومصدرا منقطعا وعيبا في المستهلك؛ فقد تحتاج الحالات استعادة ومالكين مختلفين.
import 'dart:async';
Future<void> main() async {
final source = Stream<int>.error(const FormatException('synthetic sample'));
try {
await for (final value in source) {
print(value);
}
} on FormatException {
print('stream rejected');
}
print('consumer finished');
}
stream rejected
consumer finished
المخرج المتوقع stream rejected ثم consumer finished. لا يطبع حدث بيانات. لم يُترجم أو ينفذ ولا يتصل بمصدر تدفق حقيقي.
لا يضمن الإيقاف المؤقت خفض إنتاج المصدر دائما
قد تؤجل pause تسليم الاشتراك وتسبب تخزينا إذا لم يستطع المصدر إيقاف الإنتاج الفعلي. يمكن لتراكم مستهلك بطيء بلا حد استهلاك الذاكرة رغم المظهر غير المتزامن. قد يحتاج الإلغاء تنظيفا غير متزامن؛ فانتظر Future الإلغاء عند اشتراط المالك اكتماله. يلغي الخروج من await for اشتراكها وفق سلوك اللغة ولا يعكس آثارا خارجية سابقة. اختر سعة محدودة أو سياسة إسقاط أو رفض توافق معنى الحدث، واختبر المصدر الحقيقي معزولا قبل ادعاء ضغط عكسي موثوق أو إغلاق موارد.
الأخطاء الشائعة وإصلاحها
يفشل مستمع ثان لتدفق ذي اشتراك واحد.
- لماذا يحدث؟
- يسمح عقد المصدر باشتراك واحد فقط.
- كيف تصلحه؟
- أنشئ مصدرا جديدا أو اختر مشاركة مقصودة بدلالاتها المطلوبة.
- كيف تتجنب تكراره؟
- وثّق نوع الاشتراك قبل عرض التدفق للعامة.
يفقد مستمع بث متأخر أحداثا سابقة مهمة.
- لماذا يحدث؟
- اختلط دعم المستمعين بإعادة دائمة للأحداث.
- كيف تصلحه؟
- استخدم حالة محفوظة أو آلية تسليم تحفظ التاريخ المطلوب صراحة.
- كيف تتجنب تكراره؟
- حدّد سلوك التأخر وغياب المستمع في العقد.
يراكم مشترك متوقف مؤقتا عملا بلا حد.
- لماذا يحدث؟
- افتُرض أن تأجيل التسليم يوقف كل إنتاج سابق.
- كيف تصلحه؟
- حد التخزين واختر تحكما خاصا بالمصدر أو سياسة فقد صريحة.
- كيف تتجنب تكراره؟
- تحقق من سلوك البطء وحدود الذاكرة وفق المصدر الفعلي.
اختبر فهمك
1. هل يُصلح خطأ تكرار الاستماع دائما باستدعاء asBroadcastStream؟
اعرض الحل المشروح
لا. حدّد أولا هل يحتاج المستهلكون تنفيذا جديدا مستقلا أو أحداثا لاحقة مشتركة أو تاريخا معادا أو حالة حالية. يغير البث دلالات الاشتراك وقد يفقد أحداثا أو يطيل عمر المصدر. اختر ما يحفظ مطلب المنتج وعيّن ملكية إلغاء الموارد صراحة.
2. لماذا لا تكفي طباعة onDone لإثبات تحرير مورد المصدر بأمان؟
اعرض الحل المشروح
تصف العلامة نداء واحدا لا كل فعل تنظيف أو إخفاق تحته. افحص عقد إغلاق المصدر أو إلغائه، وانتظر التنظيف المطلوب، وتحقق من الحالة المملوكة معزولا. وافحص الأخطاء والآثار الجزئية. لا تصدق إشارة النهاية تراجعا خارجيا أو تسليما دائما.
أهم ما تعلمته
- للمصدر والاشتراك والمورد أعمار مختلفة.
- تحتاج المشاركة والإعادة والضغط والإلغاء عقودا صريحة تخص المصدر.