الجزء 1. التفاعلية والتدفقات
تركز هذه السلسلة من المقالات على التفاعل وتطبيقه في JS باستخدام مكتبة رائعة مثل RxJS.
سلسلة من المقالات "أساسيات البرمجة التفاعلية باستخدام RxJS":
من هو هذا المقال : في الأساس ، سأشرح هنا الأساسيات ، لذا فإن المقالة مخصصة بشكل أساسي للمبتدئين في هذه التكنولوجيا. في الوقت نفسه ، آمل أن يتمكن المطورون المتمرسون من تعلم شيء جديد لأنفسهم. يتطلب التفاهم معرفة js (es5 / es6).
الدافع : لقد واجهت RxJS لأول مرة عندما بدأت العمل مع الزاوي. عندها واجهت صعوبة في فهم آلية التفاعل. تمت إضافة صعوبات من خلال حقيقة أنه في بداية بداية عملي ، كانت معظم المقالات مخصصة للإصدار القديم من المكتبة. اضطررت لقراءة الكثير من الوثائق والأدلة المختلفة لفهم شيء على الأقل. وفقط بعد بعض الوقت بدأت أدرك كيف "يتم ترتيب كل شيء". لجعل الحياة أسهل بالنسبة للآخرين ، قررت أن أضع كل شيء على الرفوف.
ما هو التفاعل؟
من الصعب العثور على إجابة لمصطلح شائع على ما يبدو. باختصار: التفاعل هو القدرة على الاستجابة لأي تغييرات. ولكن ما هي التغييرات التي نتحدث عنها؟ بادئ ذي بدء ، حول تغييرات البيانات. النظر في مثال:
let a = 2; let b = 3; let sum = a + b; console.log(sum);
يوضح هذا المثال نموذج البرمجة الضروري المألوف. على عكس النهج الحتمي ، فإن النهج التفاعلي يعتمد على استراتيجيات انتشار التغيير في الدفع. تتضمن استراتيجية الدفع أنه في حالة حدوث تغييرات في البيانات ، سيتم "دفع" هذه التغييرات نفسها ، وسيتم تحديث البيانات التي تعتمد عليها تلقائيًا. إليك كيفية تصرف مثالنا في حالة تطبيق استراتيجية الدفع:
let a = 2; let b = 3; let sum = a + b; console.log(sum);
هذا المثال يدل على نهج رد الفعل. تجدر الإشارة إلى أن هذا المثال لا علاقة له بالواقع ؛ لقد أعطيته فقط لإظهار الفرق في النهج. ستبدو الكود التفاعلي في التطبيقات الواقعية مختلفًا تمامًا ، وقبل الانتقال إلى الممارسة ، يجب أن نتحدث عن عنصر مهم آخر من عناصر التفاعل.
دفق البيانات
إذا نظرت إلى مصطلح "البرمجة التفاعلية" على ويكيبيديا ، فإن الموقع سوف يقدم لنا التعريف التالي: "البرمجة التفاعلية هي نموذج برمجة يركز على تدفق البيانات ونشر التغييرات." من هذا التعريف ، يمكننا أن نستنتج أن التفاعل قائم على "حوتين" رئيسيتين. لقد ذكرت توزيع التغييرات أعلاه ، لذلك لن نتعمق في ذلك. ولكن يجب أن نتحدث أكثر عن تدفقات البيانات. لنلقِ نظرة على المثال التالي:
const input = document.querySelector('input');
نستمع إلى الحدث keyup ونضع كائن الحدث في صفيفنا. بمرور الوقت ، يمكن أن تحتوي صفيفتنا على الآلاف من كائنات KeyboardEvent. تجدر الإشارة إلى أن مجموعتنا مرتبة حسب الوقت - مؤشر الأحداث اللاحقة أكبر من فهرس الأحداث السابقة. هذا الصفيف هو نموذج تدفق بيانات مبسط. لماذا مبسطة؟ لأن الصفيف يمكن فقط تخزين البيانات. يمكننا أيضًا تكرار الصفيف ومعالجة عناصره بطريقة ما. لكن الصفيف لا يمكنه إخبارنا بأنه قد تمت إضافة عنصر جديد إليه. لمعرفة ما إذا تم إضافة بيانات جديدة إلى الصفيف ، سيتعين علينا تكرارها مرة أخرى.
لكن ماذا لو عرفت صفيفتنا كيف تبلغنا أن البيانات الجديدة وصلت إليها؟ مثل هذا الصفيف يمكن أن يسمى بثبات. لذلك ، نأتي إلى تعريف التدفق. الدفق هو مجموعة من البيانات مرتبة حسب الوقت يمكن أن تشير إلى أن البيانات قد تغيرت.
يمكن ملاحظتها
والآن بعد أن عرفنا ما هي التدفقات ، فلنعمل معهم. في RxJS ، يتم تمثيل التدفقات بواسطة فئة الملاحظة. لإنشاء ساحة مشاركات خاصة بك ، ما عليك سوى الاتصال بمنشئ هذه الفئة وتمرير وظيفة الاشتراك كوسيطة لها:
const observable = new Observable(observer => { observer.next(1); observer.next(2); observer.complete(); })
عن طريق استدعاء مُنشئ الفئة التي يمكن ملاحظتها ، نقوم بإنشاء سلسلة رسائل جديدة. كحجة ، قمنا بتمرير وظيفة الاشتراك إلى المنشئ. وظيفة الاشتراك هي وظيفة عادية تأخذ مراقبًا كمعلمة. المراقب نفسه هو كائن يحتوي على ثلاث طرق:
- التالي - يلقي قيمة جديدة في الدفق
- خطأ - يلقي خطأ في الدفق ، وبعد ذلك ينتهي الدفق
- اكتمال - إنهاء الخيط
لذلك قمنا بإنشاء سلسلة رسائل تنبعث منها قيمتان وتنتهي.
الاشتراك
إذا قمنا بتشغيل الكود السابق ، فلن يحدث شيء. سننشئ دفقًا جديدًا فقط ونحفظ الرابط إليه في المتغير القابل للرصد ، لكن الدفق نفسه لن ينبعث منه أبدًا قيمة واحدة. وذلك لأن الخيوط عبارة عن أشياء "كسولة" ولا تفعل شيئًا بمفردها. لكي يبدأ التدفق الخاص بنا في إرسال قيم ويمكننا معالجة هذه القيم ، نحتاج إلى بدء "الاستماع" إلى البث. يمكنك القيام بذلك عن طريق استدعاء طريقة الاشتراك على الكائن الذي يمكن ملاحظته.
const observer = { next: value => console.log(value),
حددنا مراقبنا ووصفنا ثلاث طرق له: التالي ، خطأ ، كامل. طرق ببساطة تسجيل البيانات التي يتم تمريرها كمعلمات. ثم نسمي طريقة الاشتراك وننقل مراقبنا إليها. في لحظة الاتصال بالاشتراك ، يتم استدعاء وظيفة الاشتراك ، وهي الوظيفة التي انتقلنا إليها إلى المنشئ في مرحلة الإعلان عن التدفق. بعد ذلك ، سيتم تنفيذ رمز وظيفة الاشتراك ، والذي يمرر قيمتين إلى مراقبنا ، ثم ينهي الدفق.
بالتأكيد ، لدى الكثير منهم سؤال ، ماذا سيحدث إذا اشتركنا في البث مرة أخرى؟ كل شيء سيكون هو نفسه: سيتم تمرير الدفق مرة أخرى قيمتين إلى المراقب وتنتهي. في كل مرة يتم استدعاء طريقة الاشتراك ، سيتم استدعاء وظيفة الاشتراك ، وسيتم تنفيذ جميع الكود الخاص بها مرة أخرى. من هذا يمكننا أن نستنتج: بغض النظر عن عدد المرات التي نشترك فيها في البث ، سيتلقى مراقبونا نفس البيانات.
إلغاء الاشتراك
الآن دعونا نحاول تنفيذ مثال أكثر تعقيدًا. سوف نكتب مؤقتًا يحسب عدد الثواني من لحظة الاشتراك ، ونرسلها إلى المراقبين.
const timer = new Observable(observer => { let counter = 0;
الكود بسيط جدا. داخل وظيفة الاشتراك ، نعلن متغير العداد. ثم ، باستخدام الإغلاق ، نصل إلى المتغير من وظيفة السهم في setInterval. وفي كل ثانية نقوم بتمرير المتغير إلى المراقب ، وبعد ذلك نزيده. بعد ذلك ، اشترك في الدفق ، حدد طريقة واحدة فقط - التالي. لا تقلق من أننا لم نعلن عن طرق أخرى. أي من أساليب المراقب مطلوبة. يمكننا حتى تمرير كائن فارغ ، ولكن في هذه الحالة سيتم إهدار الخيط.
بعد الاطلاق ، سنرى السجلات المطلوبة التي ستظهر كل ثانية. إذا كنت تريد ، يمكنك تجربة والاشتراك في الدفق عدة مرات. سترى أن كل موضوع سيتم تنفيذه بشكل مستقل عن الآخرين.
إذا كنت تفكر في ذلك ، فسيتم تنفيذ سلسلة الرسائل لدينا طوال فترة التطبيق بالكامل ، لأنه ليس لدينا أي منطق لإلغاء setInterval ، وفي وظيفة الاشتراك ، لا يوجد اتصال بالطريقة الكاملة. ولكن ماذا لو كنا بحاجة إلى الخيط لإنهاء؟
في الواقع ، كل شيء بسيط للغاية. إذا نظرت إلى الوثائق ، يمكنك أن ترى أن طريقة الاشتراك تُرجع كائن اشتراك. هذا الكائن لديه طريقة إلغاء الاشتراك. نسميها ، وسيتوقف مراقبنا عن استلام القيم من الدفق.
const subscription = timer.subscribe({next: console.log}); setTimeout(() => subscription.unsubscribe(), 5000);
بعد البدء ، سنرى أن العداد يتوقف عند 4. ولكن ، على الرغم من إلغاء اشتراكنا في البث ، فإن وظيفة setInterval الخاصة بنا تستمر في العمل. إنها تزيد من عدادنا كل ثانية وتمررها إلى المراقب الوهمي. لمنع حدوث ذلك ، يجب عليك كتابة منطق إلغاء الفاصل الزمني. للقيام بذلك ، تحتاج إلى إرجاع وظيفة جديدة من وظيفة الاشتراك حيث سيتم تنفيذ منطق الإلغاء.
const timer = new Observable(observer => { let counter = 0; const intervalId = setInterval(() => { observer.next(counter++); }, 1000); return () => { clearInterval(intervalId); } });
الآن يمكننا التنفس الصعداء. بعد استدعاء طريقة إلغاء الاشتراك ، سيتم استدعاء وظيفة إلغاء الاشتراك الخاصة بنا ، والتي سوف تمسح الفاصل الزمني.
الخاتمة
توضح هذه المقالة الاختلافات بين المقاربات الحتمية والتفاعلية ، بالإضافة إلى أمثلة لإنشاء التدفقات الخاصة بك. سأتحدث في الجزء التالي عن الأساليب الأخرى لإنشاء سلاسل الرسائل الموجودة وكيفية تطبيقها.