
كيف تبدأ سريعًا مع VUE3.0: تعرف على
على الرغم من أن JavaScript أحادية الترابط، فإن حلقة الحدث تستخدم نواة النظام قدر الإمكان مما يسمح لـ Node.js بإجراء عمليات إدخال/إخراج غير محظورة. على الرغم من أن معظم النوى الحديثة متعددة الخيوط، إلا أنها يمكنها التعامل مع المهام متعددة الخيوط في ملف خلفية. عند اكتمال المهمة، تخبر النواة Node.js، ثم تتم إضافة رد الاتصال المناسب إلى الحلقة للتنفيذ. ستقدم هذه المقالة هذا الموضوع بمزيد من التفاصيل.
عندما يبدأ Node.js في التنفيذ،حلقة الحدث سيتم تهيئته أولاً، ومعالجة نص الإدخال المقدم (أو وضعه في REPL، وهو ما لم يتم تناوله في هذا المستند). سيؤدي ذلك إلى إجراء استدعاء غير متزامن لواجهة برمجة التطبيقات (API)، أو جدولة مؤقت، أو استدعاء Process.nextTick()، وبعد ذلك. بدء معالجة حلقة الحدث
يوضح الشكل التالي تسلسل تنفيذ حلقة الحدث
┌──────────────────┐.
┌─>│ الموقتات │ │ └───────────┬─────────────┘ │ ┌───────────┴─────────────┐ │ │ عمليات الاسترجاعات المعلقة │ │ └───────────┬─────────────┘ │ ┌───────────┴─────────────┐ │ │ خاملاً ، استعد │ │ └─────────┬─────────┘ ┌────────── ──────┐ │ ┌───────────┴────────────┐ │ واردة: │ │ │ استطلاع │<─────┤ اتصالات ، │ │ └───────────┬─────────────┘ │ البيانات ، إلخ. │ │ ┌───────────┴───────────────── ──────┘ │ │ تحقق │ │ └───────────┬─────────────┘ │ ┌───────────┴─────────────┐ └──┤ إغلاق عمليات الاسترجاعات │ └────────────────────────┘
ومع ذلك
، يمثل كل مربع مرحلة من حلقة الحدث
، يتم تنفيذ كل مرحلة بطريقتها الخاصة، بشكل عام، عندما تدخل حلقة الحدث إلى مرحلة ما، فإنها ستنفذ أي عمليات في المرحلة الحالية وتبدأ في تنفيذ عمليات الاسترجاعات في قائمة انتظار المرحلة الحالية حتى يتم استهلاك قائمة الانتظار بالكامل أو تنفيذها. الحد الأقصى من البيانات. عند استنفاد قائمة الانتظار أو وصولها إلى الحد الأقصى لحجمها، تنتقل حلقة الحدث إلى المرحلة التالية.
مؤقتاتالمتعلقة
تتحقق Node .js لمعرفة ما إذا كانت تنتظر الإدخال/الإخراج غير المتزامن ومؤقتًا، وإذا لم يتم ذلك، فإنها تقوم بإيقاف تشغيل
النقطة الحرجة التي سيتم عندها تنفيذ رد الاتصال، بدلاً من الوقت الذي يريده المرء سيتم تنفيذه في أقرب وقت ممكن بعد انقضاء الوقت المحدد، ومع ذلك، فإن جدولة نظام التشغيل أو عمليات الاسترجاعات الأخرى يمكن أن تؤخر التنفيذ.
من الناحية الفنية، تحدد مرحلة الاستقصاء وقت تنفيذ رد الاتصال.
على سبيل المثال، يمكنك تعيين مؤقت للتنفيذ بعد 100 مللي ثانية، لكن البرنامج النصي الخاص بك يقرأ ملفًا بشكل غير متزامن ويستغرق 95 مللي ثانية
const fs = require('fs') ;
وظيفة someAsyncOperation(رد الاتصال) {
// افترض أن هذا يستغرق 95 مللي ثانية حتى يكتمل
fs.readFile('/path/to/file', رد الاتصال);
}
const timeoutScheduled = Date.now();
setTimeout(() => {
تأخير ثابت = Date.now() - timeoutScheduled;
console.log(`لقد مرت مللي ثانية منذ أن تم جدولتي`);
}, 100);
// قم بإجراء someAsyncOperation الذي يستغرق 95 مللي ثانية لإكماله
someAsyncOperation(() => {
const startCallback = Date.now();
// افعل شيئًا سيستغرق 10 مللي ثانية ...
بينما (Date.now() - startCallback < 10) {
// لا تفعل شيئًا
}
}); عندما تدخل حلقة الحدث مرحلة الاستقصاء، تكون هناك قائمة انتظار فارغة (لم يكتمل fs.readFile() بعد)، لذا سينتظر المللي ثانية المتبقية حتى يتم الوصول إلى الحد الأقصى للمؤقت بعد 95 مللي ثانية أكمل .readFile() قراءة الملف وسيستغرق 10 مللي ثانية لإضافته إلى مرحلة الاستقصاء وإكمال التنفيذ. عند اكتمال رد الاتصال، لا توجد عمليات رد اتصال في قائمة الانتظار ليتم تنفيذها، وتعود حلقة الحدث إلى مرحلة المؤقتات. لتنفيذ رد الاتصال الموقت. في هذا المثال، سترى أن المؤقت يتأخر لمدة 105 مللي ثانية قبل التنفيذ
لمنع مرحلة الاستقصاء من حظر حلقة الحدث، كما تحتوي مكتبة libuv (مكتبة لغة C التي تنفذ حلقة الحدث وجميع السلوكيات غير المتزامنة على النظام الأساسي). مرحلة الاستقصاء. الحد الأقصى لإيقاف استقصاء المزيد من الأحداث
تقوم هذه المرحلة بتنفيذ عمليات الاسترجاعات لعمليات نظام معينة (مثل أنواع أخطاء TCP). على سبيل المثال، تريد بعض أنظمة *nix الانتظار حتى يتم الإبلاغ عن خطأ إذا تلقى مقبس TCP ECONNREFUSED عند محاولة الاتصال. سيتم وضع هذا في قائمة الانتظار للتنفيذ أثناء مرحلة رد الاتصال المعلقة.
تحتوي مرحلةعلى وظيفتين رئيسيتين
عندما تدخل حلقة الحدث مرحلة الاستقصاء ولا يوجد مؤقت، يحدث الأمران التاليان
بمجرد أن تكون قائمة انتظار الاستقصاء فارغة سيكتشف ما إذا كان المؤقت قد انتهى أم لا، إذا كان الأمر كذلك، فستصل حلقة الحدث إلى مرحلة المؤقتات وتنفذ
في هذه المرحلة، مما يسمح للأشخاص بتنفيذ عمليات رد الاتصال فور اكتمال مرحلة الاستقصاء. إذا أصبحت مرحلة الاستقصاء خاملة وتم وضع البرنامج النصي في قائمة الانتظار باستخدام setImmediate()، فقد تستمر حلقة الحدث في مرحلة التحقق بدلاً من الانتظار.
setImmediate() هو في الواقع مؤقت خاص يعمل في مرحلة منفصلة من حلقة الحدث. ويستخدم واجهة برمجة تطبيقات libuv لجدولة عمليات رد الاتصال ليتم تنفيذها بعد اكتمال مرحلة الاستقصاء.
عادةً، أثناء تنفيذ التعليمات البرمجية، تصل حلقة الحدث في النهاية إلى مرحلة الاستقصاء، حيث تنتظر الاتصالات الواردة والطلبات وما إلى ذلك. ومع ذلك، إذا تمت جدولة رد اتصال باستخدام setImmediate() وأصبحت مرحلة الاستقصاء خاملة، فسوف تنتهي وتستمر في مرحلة التحقق بدلاً من انتظار حدث الاستقصاء.
إذا تم إغلاق مأخذ توصيل أو عملية فجأة (على سبيل المثال، المقبس.destroy())، فسيتم إرسال حدث الإغلاق إلى هذه المرحلة، وإلا فسيتم إرساله عبرprocess.nextTick() setImmediate
setImmediate() و setTimeout () متشابهان، لكنهما يتصرفان بشكل مختلف اعتمادًا على وقت استدعائهما،
يعتمد الترتيب الذي يتم به تنفيذ كل رد اتصال على ترتيب استدعائها في هذه البيئة، إذا تم استدعاء نفس الوحدة في نفس الوقت، فسيكون الوقت محدودًا بأداء العملية (سيتأثر هذا أيضًا بالتطبيقات الأخرى التي تعمل على هذا الجهاز،
على سبيل المثال
).، إذا لم نقم بتشغيل البرنامج النصي التالي في الإدخال / الإخراج، على الرغم من تأثره بأداء العملية، فلا يمكن تحديد ترتيب تنفيذ هذين المؤقتين:
// timeout_vs_immediate.js
setTimeout(() => {
console.log('المهلة');
}, 0);
setImmediate(() => {
console.log('فوري');
}); عقدة $ timeout_vs_immediate.js نفذ الوقت مباشر $ العقدة timeout_vs_immediate.js مباشرومع ذلك
،
إذا انتقلت إلى حلقة الإدخال/الإخراج، فسيتم دائمًا تنفيذ رد الاتصال الفوري أولاً
// timeout_vs_immediate.js
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('المهلة');
}, 0);
setImmediate(() => {
console.log('فوري');
});
}); عقدة $ timeout_vs_immediate.js مباشر نفذ الوقت $ العقدة timeout_vs_immediate.js مباشرتتمثل ميزة
المهلة
setImmediate عبر setTimeout في أنه سيتم دائمًا تنفيذ setImmediate أولاً قبل أي مؤقت في الإدخال/الإخراج، بغض النظر عن عدد المؤقتات الموجودة.
على الرغم من أنnextTick() هي جزء من واجهة برمجة التطبيقات غير المتزامنة، إلا أنك ربما لاحظت أنها لا تظهر في الرسم التخطيطي، وذلك لأن العملية.nextTick() ليست جزءًا من تقنية حلقة الأحداث. يتم تنفيذ العملية الحالية بعد الانتهاء، سيتم تنفيذ nextTickQueue بغض النظر عن المرحلة الحالية من حلقة الحدث. هنا، يتم تعريف العمليات على أنها تحويلات من معالج C/C++ الأساسي والتعامل مع JavaScript الذي يجب تنفيذه. وفقًا للرسم التخطيطي، يمكنك استدعاء التابعprocess.nextTick() في أي مرحلة. سيتم تنفيذ جميع عمليات الاسترجاعات التي تم تمريرها إلى التابعprocess.nextTick() قبل أن تستمر حلقة الحدث في التنفيذ. وقد يؤدي ذلك إلى بعض المواقف السيئة لأنه يسمح لك بالاتصال بشكل متكرر يقوم التابعprocess.nextTick() "بمنع" الإدخال/الإخراج، مما يمنع حلقة الحدث من الدخول إلى مرحلة الاستقصاء.
لماذا تم تضمين هذا الموقف في Node.js؟ نظرًا لأن فلسفة تصميم Node.js هي أن واجهة برمجة التطبيقات (API) يجب أن تكون دائمًا غير متزامنة حتى لو لم تكن كذلك، فانظر إلى
وظيفة المقتطف التالية apiCall(arg, callback) {
إذا (نوع الوسيطة !== 'سلسلة')
عملية الإرجاع.nextTick(
أتصل مرة أخرى،
خطأ TypeError جديد ("يجب أن تكون الوسيطة عبارة عن سلسلة")
);
} يقوم المقتطف بالتحقق من المعلمات وإذا كان غير صحيح، فإنه يمرر الخطأ إلى رد الاتصال. تم تحديث واجهة برمجة التطبيقات (API) مؤخرًا للسماح بتمرير المعلمات إلى العملية.nextTick() مما يسمح لها بقبول أي معلمات تم تمريرها بعد رد الاتصال كوسائط لرد الاتصال، لذلك لا يتعين عليك دمج الوظائف.
ما نقوم به هو تمرير الخطأ مرة أخرى إلى المستخدم، ولكن فقط إذا سمحنا بتنفيذ بقية التعليمات البرمجية الخاصة بالمستخدم. باستخدام العملية.nextTick()، نضمن أن apiCall() يقوم دائمًا بتشغيل رد الاتصال الخاص به بعد بقية كود المستخدم وقبل السماح بمتابعة حلقة الحدث. ولتحقيق ذلك، يُسمح لمكدس استدعاء JS بالاسترخاء ثم يتم تنفيذ رد الاتصال المقدم على الفور، مما يسمح للمرء بإجراء استدعاءات متكررة إلىprocess.nextTick() دون الضغط على RangeError: تم تجاوز الحد الأقصى لحجم مكدس الاستدعاءات اعتبارًا من الإصدار 8.