أساسيات الـ Streams
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 77 من 110.
بينما يقدم Future قيمة واحدة، يقدم Stream سلسلة من القيم بمرور الوقت. فكر فيه كأنه أنبوب يمكنه إرسال قطع متعددة من البيانات - وهو مثالي للأحداث، أو التحديثات في الوقت الفعلي، أو معالجة البيانات قطعة بقطعة.
يمكنك إنشاء تدفق (stream) بسيط باستخدام Stream.fromIterable() أو دالة مولد غير متزامنة باستخدام async* و yield:
Stream<int> countToThree() async* {
yield 1;
yield 2;
yield 3;
}
Future<void> main() async {
await for (var number in countToThree()) {
print(number);
}
}
// المخرجات: 1, 2, 3تحدد الكلمة الأساسية async* دالة تُرجع Stream، وتقوم yield بإصدار كل قيمة. لاستهلاك الـ stream، استخدم await for التي تعالج كل قيمة فور وصولها.
يمكنك أيضاً الاستماع إلى دفق (stream) باستخدام التابع listen()، والذي يأخذ دالة استدعاء (callback function):
Stream<String> greetings() async* {
yield 'Hello';
yield 'Hi';
yield 'Hey';
}
void main() {
greetings().listen((message) {
print(message);
});
}الفرق الجوهري عن Futures: يكتمل Future مرة واحدة بقيمة واحدة، بينما يمكن لـ Stream إرسال قيم متعددة قبل أن ينتهي. وهذا يجعل Streams مثالية للتعامل مع البيانات المستمرة مثل تفاعلات المستخدم، أو قراءات المستشعرات، أو البيانات التي تصل في أجزاء.
التحدي
سهللنقم ببناء نظام مؤقت تنازلي يرسل القيم بمرور الوقت باستخدام الـ streams! ستقوم بإنشاء فئة (class) تولد تسلسلاً من أرقام العد التنازلي، مما يوضح كيف تقوم الـ streams بتقديم قيم متعددة بدلاً من قيمة واحدة فقط.
ستقوم بتنظيم الكود الخاص بك في ملفين:
countdown.dart: أنشئ فئةCountdownالتي تولد streams للعد التنازلي. يجب أن تحتوي الفئة الخاصة بك على:- حقل
startمن نوع (int) يخزن رقم البداية للعد التنازلي - مشد (constructor) يأخذ رقم البداية
- دالة
countDown()تعيدStream<int>. استخدمasync*وyieldلإرسال الأرقام منstartنزولاً إلى0(متضمناً الصفر) - دالة
countDownWithMessage()تعيدStream<String>. يجب أن ترسل هذه الدالة سلاسل نصية بتنسيق'T-minus [number]'لكل رقم منstartنزولاً إلى1، ثم ترسل'Liftoff!'كقيمة نهائية
- حقل
main.dart: قم باستيراد ملف العد التنازلي الخاص بك واستعرض كلتا دالتي الـ stream. يجب أن تكون الدالة الرئيسية (main function) من نوع async وتقوم بما يلي:- إنشاء مثيل (instance) من
Countdownيبدأ من3 - طباعة
Number countdown: - استخدام
await forللتكرار عبرcountDown()وطباعة كل رقم - طباعة سطر فارغ
- طباعة
Launch sequence: - استخدام
await forللتكرار عبرcountDownWithMessage()وطباعة كل رسالة
- إنشاء مثيل (instance) من
لاحظ كيف تقوم كل عبارة yield بإرسال قيمة عبر الـ stream، وكيف تقوم await for بمعالجة كل قيمة فور وصولها. على عكس الـ Future الذي يقدم نتيجة واحدة، ستقوم الـ streams الخاصة بك بإرسال قيم متعددة بالتتابع!
المخرجات المتوقعة:
Number countdown:
3
2
1
0
Launch sequence:
T-minus 3
T-minus 2
T-minus 1
Liftoff!ورقة مرجعية
يوفر الـ Stream سلسلة من القيم بمرور الوقت، على عكس الـ Future الذي يوفر قيمة واحدة فقط. تعد الـ Streams مثالية للأحداث، أو التحديثات في الوقت الفعلي، أو معالجة البيانات قطعة بقطعة.
قم بإنشاء stream باستخدام دالة مولد غير متزامنة (async generator function) مع async* و yield:
Stream<int> countToThree() async* {
yield 1;
yield 2;
yield 3;
}استهلك الـ stream باستخدام await for:
Future<void> main() async {
await for (var number in countToThree()) {
print(number);
}
}بدلاً من ذلك، استخدم التابع listen() مع دالة استدعاء (callback):
void main() {
greetings().listen((message) {
print(message);
});
}يمكنك أيضاً إنشاء stream من iterable باستخدام Stream.fromIterable().
جرّب بنفسك
import 'countdown.dart';
Future<void> main() async {
// TODO: قم بإنشاء مثيل Countdown يبدأ من 3
// TODO: اطبع 'Number countdown:'
// TODO: استخدم await for للتكرار عبر countDown() وطباعة كل رقم
// TODO: اطبع سطرًا فارغًا
// TODO: اطبع 'Launch sequence:'
// TODO: استخدم await for للتكرار عبر countDownWithMessage() وطباعة كل رسالة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع