مقدمة
يتم تنفيذ JavaScript في الغالب على مؤشر ترابط واحد في Node.js وفي المتصفح (مع بعض الاستثناءات مثل سلاسل العمليات، والتي تقع خارج نطاق المقالة الحالية). في هذه المقالة سأحاول شرح آلية التزامن في Node.js وهي حلقة الأحداث.
قبل البدء في قراءة هذا المقال يجب أن تكون على دراية بالمكدس وكيفية عمله، لقد كتبت في الماضي عن هذه الفكرة، لذا تحقق من Stack & Heap — لا تبدأ في البرمجة دون فهمها — Moshe Binieli | واسطة
صورة المقدمة
أمثلة
أعتقد أن التعلم هو الأفضل من خلال الأمثلة، لذلك سأبدأ بأربعة أمثلة بسيطة على التعليمات البرمجية. سوف أقوم بتحليل الأمثلة ثم سأتعمق في بنية Node.js.
مثال 1:
console.log(1);
console.log(2);
console.log(3);
// الإخراج:
// 1
// 2
// 3
هذا المثال سهل جدًا، في الخطوة الأولى، ينتقل console.log(1) إلى مكدس الاستدعاءات، ويتم تنفيذه ثم إزالته، وفي الخطوة الثانية، ينتقل console.log(2) إلى مكدس الاستدعاءات، ويتم تنفيذه ثم تمت إزالته، وهكذا بالنسبة لـ console.log(3).
تصور مكدس الاستدعاءات للمثال 1
مثال 2:
console.log(1);
setTimeout(وظيفة foo(){
console.log(2);
}, 0);
console.log(3);
// الإخراج:
// 1
// 3
// 2
يمكننا أن نرى في هذا المثال أننا قمنا بتشغيل setTimeout على الفور، لذلك نتوقع أن يكون console.log(2) قبل console.log(3)، ولكن هذا ليس هو الحال ودعونا نفهم الآلية الكامنة وراء ذلك.
البنية الأساسية لحلقة الأحداث (سنتعمق فيها أكثر لاحقًا)
Stack & Heap: راجع مقالتي حول هذا المقال (لقد أضفت رابطًا في بداية هذه المقالة)
Web APIs: وهي مدمجة في متصفح الويب الخاص بك، وهي قادرة على كشف البيانات من المتصفح وبيئة الكمبيوتر المحيطة والقيام بأشياء معقدة مفيدة باستخدامها. إنها ليست جزءًا من لغة JavaScript نفسها، بل إنها مبنية على لغة JavaScript الأساسية، مما يوفر لك قوى خارقة إضافية لاستخدامها في كود JavaScript الخاص بك. على سبيل المثال، توفر واجهة برمجة تطبيقات تحديد الموقع الجغرافي بعض بنيات JavaScript البسيطة لاسترداد بيانات الموقع حتى تتمكن من تحديد موقعك على خريطة Google. في الخلفية، يستخدم المتصفح في الواقع بعض التعليمات البرمجية المعقدة ذات المستوى الأدنى (مثل C ) للتواصل مع أجهزة نظام تحديد المواقع العالمي (GPS) بالجهاز (أو أي شيء متاح لتحديد بيانات الموقع)، واسترداد بيانات الموقع، وإعادتها إلى بيئة المتصفح لاستخدامها. في التعليمات البرمجية الخاصة بك. ولكن مرة أخرى، يتم تجريد هذا التعقيد منك بواسطة واجهة برمجة التطبيقات.
حلقة الأحداث وقائمة انتظار رد الاتصال: يتم نقل الوظائف التي أنهت تنفيذ Web Apis إلى قائمة انتظار رد الاتصال، وهي بنية بيانات قائمة انتظار عادية، وحلقة الأحداث مسؤولة عن فصل الوظيفة التالية من قائمة انتظار رد الاتصال وإرسال الوظيفة إلى مكدس الاستدعاءات لتنفيذ الوظيفة.
ترتيب التنفيذ
يتم تنفيذ جميع الوظائف الموجودة حاليًا في مكدس الاستدعاءات ثم يتم إخراجها من مكدس الاستدعاءات.
عندما تكون مكدس الاستدعاءات فارغًا، يتم وضع جميع المهام الموضوعة في قائمة الانتظار على مكدس الاستدعاءات واحدة تلو الأخرى ويتم تنفيذها، ثم يتم إخراجها من مكدس الاستدعاءات.
دعونا نفهم المثال 2
console.log(1) ووضعه على مكدس الاستدعاءات ويتم تنفيذه.
لذا فإن معلمة التأخير في setTimeout(function, Delay) لا تمثل التأخير الزمني الدقيق الذي يتم بعده تنفيذ الوظيفة. إنه يشير إلى الحد الأدنى من وقت الانتظار وبعد ذلك سيتم تنفيذ الوظيفة في وقت ما.
console.log(1);
setTimeout(وظيفة foo() {
console.log('foo');
}، 3500)؛
setTimeout(function boo() {
console.log('boo');
}، 1000)؛
console.log(2);
// الإخراج:
// 1
// 2
// 'بوو'
// 'فو'
مثال 4:
console.log(1);
setTimeout(وظيفة foo() {
console.log('foo');
}، 6500)؛
setTimeout(function boo() {
console.log('boo');
}، 2500)؛
setTimeout(وظيفة باز() {
console.log('baz');
}, 0);
لـ (القيمة الثابتة لـ ['A'، 'B']) {
console.log(value);
الوظيفة الثانية () {
console.log(2);
اثنين()؛
// الإخراج:
// 1
// 'أ'
// 'ب'
// 2
// 'باز'
// 'بوو'
// 'فو'
تستمر حلقة الحدث في تنفيذ جميع عمليات الاسترجاعات المنتظرة في قائمة انتظار المهام. داخل قائمة انتظار المهام، يتم تصنيف المهام على نطاق واسع إلى فئتين، وهما المهام الصغيرة والمهام الكلية.
ولكي نكون أكثر دقة، هناك في الواقع نوعان من قوائم الانتظار.
المهام الكلية الشائعة هي setTimeout وsetInterval وsetImmediate.
المهام الصغيرة الشائعة هي Process.nextTick وPromise callback.
ترتيب التنفيذ
يتم تنفيذ جميع الوظائف الموجودة حاليًا في مكدس الاستدعاءات ثم يتم إخراجها من مكدس الاستدعاءات.
عندما تكون مكدس الاستدعاءات فارغًا، يتم وضع جميع المهام الصغيرة الموضوعة في قائمة الانتظار على مكدس الاستدعاءات واحدة تلو الأخرى ويتم تنفيذها، ثم يتم إخراجها من مكدس الاستدعاءات.
عندما تكون كل من قائمة انتظار مكدس الاستدعاءات وقائمة انتظار المهام الصغيرة فارغة، يتم وضع جميع المهام الكلية الموجودة في قائمة الانتظار على مكدس الاستدعاءات واحدة تلو الأخرى ويتم تنفيذها، ثم يتم إخراجها من مكدس الاستدعاءات.
مثال 5:
console.log(1);
setTimeout(وظيفة foo() {
console.log('foo');
}, 0);
وعد.الحل ()
.ثم (وظيفة بوو () {
console.log('boo');
});
console.log(2);
// الإخراج:
// 1
// 2
// 'بوو'
// 'فو'
يتم استدعاء الأسلوب console.log(1) ووضعه في مكدس الاستدعاءات ويتم تنفيذه.
يتم تنفيذ SetTimeout، ويتم نقل console.log('foo') إلى SetTimeout Web Api، وبعد 0 مللي ثانية ينتقل إلى قائمة انتظار المهام الماكرو.
يتم استدعاء Promise.resolve()، ويتم حلها ثم يتم نقل طريقة .then() إلى قائمة انتظار المهام الصغيرة.
يتم استدعاء الأسلوب console.log(2) ووضعه في مكدس الاستدعاءات ويتم تنفيذه.
ترى حلقة الأحداث أن مكدس الاستدعاءات فارغًا، فهي تأخذ أولاً المهمة من قائمة انتظار المهام الصغيرة وهي مهمة الوعد، وتضع console.log('boo') في مكدس الاستدعاءات وتنفذها.
ترى حلقة الأحداث أن مكدس الاستدعاءات فارغًا، ثم ترى أن المهمة الصغيرة فارغة، ثم تأخذ المهمة التالية من قائمة انتظار المهام الكلية وهي مهمة SetTimeout، وتضع console.log('foo') على مكدس الاستدعاءات وتنفيذها.
فهم متقدم لحلقة الأحداث
كنت أفكر في الكتابة عن المستوى المنخفض لكيفية عمل آلية Event Loop، فمن الممكن أن يكون تدوينة بحد ذاتها، فقررت أن أضع مقدمة للموضوع وأرفق روابط جيدة تشرح الموضوع بعمق.
عندما يبدأ Node.js، فإنه يقوم بتهيئة حلقة الحدث، ومعالجة نص الإدخال المقدم (أو يسقط في REPL) والذي قد يقوم باستدعاءات واجهة برمجة التطبيقات غير المتزامنة، أو جدولة مؤقتات، أو استدعاء Process.nextTick()، ثم يبدأ في معالجة حلقة الحدث.
نظرة عامة مبسطة على ترتيب العمليات في حلقة الأحداث
تحتوي كل مرحلة على قائمة انتظار FIFO من عمليات الاسترجاعات المطلوب تنفيذها (أقول ذلك بعناية هنا لأنه قد يكون هناك بنية بيانات أخرى تعتمد على التنفيذ). في حين أن كل مرحلة خاصة بطريقتها الخاصة، بشكل عام، عندما تدخل حلقة الحدث إلى مرحلة معينة، فإنها ستنفذ أي عمليات خاصة بتلك المرحلة، ثم تنفذ عمليات الاسترجاعات في قائمة انتظار تلك المرحلة حتى يتم استنفاد قائمة الانتظار أو الحد الأقصى لعدد عمليات الاسترجاعات تم تنفيذه. عند استنفاد قائمة الانتظار أو الوصول إلى حد رد الاتصال، ستنتقل حلقة الحدث إلى المرحلة التالية، وهكذا.
الموقتات: تنفذ هذه المرحلة عمليات رد الاتصال المجدولة بواسطة setTimeout() وsetInterval().
عمليات الاسترجاعات المعلقة: تنفيذ عمليات رد الاتصال للإدخال/الإخراج المؤجلة إلى تكرار الحلقة التالية.
خامل، تحضير: يستخدم داخليًا فقط.
الاستطلاع: استرداد أحداث الإدخال/الإخراج الجديدة؛ تنفيذ عمليات الاسترجاعات ذات الصلة بالإدخال/الإخراج (جميعها تقريبًا باستثناء عمليات الاسترجاعات القريبة، وتلك المجدولة بواسطة المؤقتات، وsetImmediate())؛ سيتم حظر العقدة هنا عندما يكون ذلك مناسبًا.
تحقق: يتم استدعاء عمليات رد الاتصال setImmediate() هنا.
عمليات الاسترجاعات القريبة: بعض عمليات الاسترجاعات القريبة، على سبيل المثال. المقبس.on('إغلاق', ...).
كيف تتناسب الخطوات السابقة هنا؟
لذا فإن الخطوات السابقة مع "قائمة انتظار رد الاتصال" فقط ومن ثم مع "قوائم انتظار الماكرو والصغرى" كانت عبارة عن تفسيرات مجردة حول كيفية عمل حلقة الأحداث.
الخطوة 1: تقوم حلقة الحدث بتحديث وقت الحلقة إلى الوقت الحالي للتنفيذ الحالي.
الخطوة 2: يتم تنفيذ قائمة الانتظار الصغيرة.
الخطوة 3: يتم تنفيذ مهمة من مرحلة المؤقتات.
الخطوة 4: التحقق مما إذا كان هناك شيء ما في قائمة الانتظار الصغيرة وتنفيذ قائمة الانتظار الصغيرة بأكملها إذا كان هناك شيء ما.
الخطوة 5: العودة إلى الخطوة 3 حتى تصبح مرحلة المؤقتات فارغة.
الخطوة 6: يتم تنفيذ مهمة من مرحلة عمليات رد الاتصال المعلقة.
الخطوة 7: التحقق مما إذا كان هناك شيء ما في قائمة الانتظار الصغيرة وتنفيذ قائمة الانتظار الصغيرة بأكملها إذا كان هناك شيء ما.
الخطوة 8: العودة إلى الخطوة 6 حتى تصبح مرحلة عمليات رد الاتصال المعلقة فارغة.
ثم خامل... قائمة انتظار صغيرة... استطلاع... قائمة انتظار صغيرة... تحقق... قائمة انتظار صغيرة... أغلق عمليات الاسترجاعات ثم يبدأ من جديد.
لذلك قدمت نظرة عامة لطيفة حول كيفية عمل حلقة الأحداث خلف الكواليس، وهناك الكثير من الأجزاء المفقودة التي لم أذكرها هنا لأن التوثيق الفعلي يقوم بعمل رائع في شرحه، وسأقدم روابط رائعة لـ التوثيق، أشجعك على استثمار 10-20 دقيقة وفهمها.
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3