كيفية البدء بسرعة باستخدام VUE3.0: تعرف
على سياق التنفيذ ومكدس التنفيذ وآلية التنفيذ (المهام المتزامنة والمهام غير المتزامنة والمهام الدقيقة والمهام الكبيرة وحلقات الأحداث) في js
وهي نقطة اختبار متكررة في المقابلات بعض الأصدقاء قد تشعر بالارتباك عند سؤالك، لذلك سألخصها اليوم، على أمل أن تكون مفيدة لك أمام الشاشة.
قبل الحديث عن سياق التنفيذ وآلية تنفيذ js
في js
، دعنا نتحدث عن الخيوط والعمليات،
بالمصطلحات الرسمية،线程
أصغر وحدة لجدولة وحدة المعالجة CPU
.
؟ من الناحية الرسمية،进程
هي أصغر وحدة لتخصيص موارد CPU
.
线程
عبارة عن وحدة تشغيل برنامج تعتمد على进程
. في مصطلحات الشخص العادي،线程
هو تدفق تنفيذ في البرنامج. يمكن أن تحتوي进程
على线程
واحد أو أكثر.
يوجد تدفق تنفيذ واحد فقط في进程
يسمى单线程
، أي أنه عند تنفيذ البرنامج، يتم ترتيب مسارات البرنامج المتخذة بترتيب متتابع، ويجب معالجة المسارات السابقة قبل تنفيذ المسارات اللاحقة.
تسمى تدفقات التنفيذ المتعددة في进程
多线程
، أي أنه يمكن تشغيل عدة线程
مختلفة في وقت واحد في برنامج لأداء مهام مختلفة، مما يعني أنه يُسمح لبرنامج واحد بإنشاء线程
تنفيذ متوازية لإكمال المهام الخاصة بكل منها. .
سيقدم المؤلف مثالًا بسيطًا أدناه، على سبيل المثال، إذا فتحنا qq音乐
واستمعنا إلى الأغاني، فيمكن فهم qq音乐
على أنها عملية qq音乐
يمكننا تنزيلها أثناء الاستماع إلى الأغاني للأغاني عبارة عن خيط، والتنزيل عبارة عن خيط. إذا فتحنا vscode
مرة أخرى لكتابة التعليمات البرمجية، فستكون هناك عملية أخرى.
العمليات مستقلة عن بعضها البعض، ولكن يتم مشاركة بعض الموارد بين سلاسل العمليات ضمن نفس العملية.
تمر دورة حياة الخيط بخمس مراحل.
الحالة الجديدة: بعد استخدام الكلمة الأساسية new
وفئة Thread
أو فئتها الفرعية لإنشاء كائن مؤشر ترابط، يصبح كائن مؤشر الترابط في الحالة الجديدة. ويبقى على هذه الحالة حتى start()
الخيط.
حالة الاستعداد: عندما يستدعي كائن مؤشر الترابط طريقة start()
، يدخل مؤشر الترابط إلى حالة الاستعداد. الخيط في حالة الاستعداد موجود في قائمة الانتظار الجاهزة ويمكن تشغيله على الفور طالما أنه يحصل على الحق في استخدام CPU
.
حالة التشغيل: إذا حصل الخيط في حالة الاستعداد على موارد CPU
، فيمكنه تنفيذ run()
، ويكون الخيط في حالة التشغيل. الخيط في حالة التشغيل هو الأكثر تعقيدًا، ويمكن أن يصبح مسدودًا وجاهزًا وميتًا.
حالة الحظر: إذا نفذ الخيط وضع sleep(睡眠)
suspend(挂起)
wait(等待)
وطرق أخرى، بعد فقدان الموارد المشغولة، سيدخل الخيط في حالة الحظر من حالة التشغيل. يمكن إعادة إدخال حالة الاستعداد بعد انتهاء وقت السكون أو الحصول على موارد الجهاز. يمكن تقسيمها إلى ثلاثة أنواع:
حظر الانتظار: ينفذ الخيط في حالة التشغيل طريقة wait()
، مما يتسبب في دخول الخيط إلى حالة الحظر قيد الانتظار.
الحظر المتزامن: فشل مؤشر الترابط في الحصول على قفل المزامنة synchronized
(لأن قفل المزامنة مشغول بسلاسل رسائل أخرى).
حظر آخر: عندما يتم إصدار طلب I/O
عن طريق استدعاء sleep()
أو join()
لمؤشر الترابط ، سيدخل مؤشر الترابط في حالة الحظر. عند انتهاء مهلة حالة sleep()
، تنتظر join()
حتى ينتهي مؤشر الترابط أو تنتهي مهلته، أو تكتمل معالجة I/O
، ويعود مؤشر الترابط إلى حالة الاستعداد.
حالة الموت: عندما يكمل مؤشر ترابط قيد التشغيل مهمته أو تحدث ظروف إنهاء أخرى، يتحول مؤشر الترابط إلى حالة الإنهاء.
JS
باعتبارها لغة برمجة للمتصفح، يتم استخدام JS
بشكل أساسي للتفاعل مع المستخدمين وتشغيل DOM
. وهذا يحدد أنه لا يمكن أن يكون إلا بخيط واحد، وإلا فإنه سوف يسبب مشاكل مزامنة معقدة للغاية. على سبيل المثال، لنفترض أن JavaScript
يحتوي على خيطين في نفس الوقت، حيث يقوم أحدهما بإضافة محتوى إلى عقدة DOM
معينة، ويقوم الخيط الآخر بحذف العقدة، في هذه الحالة، أي مؤشر ترابط يجب أن يستخدمه المتصفح؟
عندما يقوم محرك JS
بتحليل جزء من التعليمات البرمجية القابلة للتنفيذ (عادةً مرحلة استدعاء الوظيفة)، فإنه سيقوم أولاً ببعض الأعمال التحضيرية قبل التنفيذ. (سياق التنفيذ (يشار إليه باسم EC
)" أو يمكن أن يطلق عليه أيضًا بيئة التنفيذ .
هناك ثلاثة أنواع من سياق التنفيذ في javascript
، وهي:
سياق التنفيذ العام . هذا هو سياق التنفيذ الافتراضي أو الأساسي. سيكون هناك سياق عالمي واحد فقط في البرنامج، وسيكون موجودًا طوال دورة حياة البرنامج لن يتم إتلاف الجزء السفلي من مكدس javascript
عن طريق ظهور المكدس. سيقوم السياق العام بإنشاء كائن عام (مع أخذ بيئة المتصفح كمثال، هذا الكائن العام هو window
)، وربط this
القيمة بهذا الكائن العام.
سياق تنفيذ الوظيفة عندما يتم استدعاء وظيفة، يتم إنشاء سياق تنفيذ وظيفة جديد (بغض النظر عما إذا تم استدعاء الوظيفة بشكل متكرر).
سياق تنفيذ وظيفة التقييم سيكون للتعليمات البرمجية التي يتم تنفيذها داخل وظيفة eval
أيضًا سياق التنفيذ الخاص بها، ولكن نظرًا لعدم استخدام eval
كثيرًا، فلن يتم تحليله هنا.
ذكرنا سابقًا أن js
ستنشئ سياق تنفيذ عند تشغيله، ولكن يجب تخزين سياق التنفيذ، فما الذي يتم استخدامه لتخزينه؟ تحتاج إلى استخدام بنية بيانات المكدس.
المكدس عبارة عن بنية بيانات تدخل أولاً وتخرج أخيرًا.
باختصار، سياق التنفيذ المستخدم لتخزين سياق التنفيذ الذي تم إنشاؤه عند تشغيل التعليمات البرمجية هو مكدس التنفيذ .
عند تنفيذ جزء من التعليمات البرمجية JS
بعد ذلك، سيقوم محرك JS
بإنشاء سياق تنفيذ عالمي push
إلى مكدس التنفيذ. في هذه العملية، سيقوم محرك JS
بتخصيص الذاكرة لجميع المتغيرات في هذا الرمز وتعيين قيمة أولية (غير محددة). سيدخل محرك JS
في مرحلة التنفيذ، في هذه العملية سيقوم محرك JS
بتنفيذ التعليمات البرمجية سطرًا تلو الآخر، أي تعيين قيم (قيم حقيقية) للمتغيرات التي تم تخصيص الذاكرة لها واحدًا تلو الآخر.
إذا كان هناك استدعاء function
في هذا الكود، فسيقوم محرك JS
بإنشاء سياق تنفيذ الوظيفة push
إلى مكدس التنفيذ، وتكون عملية الإنشاء والتنفيذ هي نفس سياق التنفيذ العام.
عند اكتمال مكدس التنفيذ، سينبثق سياق التنفيذ من المكدس، ثم سيتم إدخال سياق التنفيذ التالي.
اسمحوا لي أن أقدم مثالاً أدناه إذا كان لدينا الكود التالي في برنامجنا:
console.log("Global Execution context start"); الوظيفة أولاً () { console.log("الوظيفة الأولى"); ثانية()؛ console.log("الوظيفة الأولى مرة أخرى"); } الوظيفة الثانية () { console.log("الوظيفة الثانية"); } أولاً()؛ console.log("نهاية سياق التنفيذ العالمي");
دعنا نحلل المثال أعلاه بإيجاز.
أولاً، سيتم إنشاء مكدس تنفيذ
، ثم سيتم إنشاء سياق عام، وسيتم push
سياق التنفيذ إلى مكدس التنفيذ
لبدء التنفيذ. ، Global Execution Context start
يواجه الطريقة first
، وينفذ الطريقة، وينشئ سياق تنفيذ الوظيفة push
إلى مكدس التنفيذ
لتنفيذ سياق التنفيذ first
first function
وتواجه الوظيفة المخرجة الأولى الطريقة second
، وتنفذ الطريقة. ، ينشئ سياق تنفيذ الوظيفة push
إلى مكدس التنفيذ لتنفيذ
سياق second
second function
، ويتم تنفيذ سياق التنفيذ second
، وإخراجه من المكدس، وإدخال سياق التنفيذ الأول first
ويستمر سياق التنفيذ first
التنفيذ والإخراج تم تنفيذ Again first function
first
وبرزت من المكدس، وأدخلت سياق التنفيذ التالي، سياق التنفيذ العالمي،
سياق التنفيذ العالمي، متابعة التنفيذ والإخراج Global Execution Context end
نستخدم صورة لتلخيصها.
على ما يرام. بعد الحديث عن سياق التنفيذ ومكدس التنفيذ، دعونا نتحدث عن آلية تنفيذ js. وبالحديث عن آلية تنفيذ js
js
إلى فهم المهام المتزامنة والمهام غير المتزامنة والمهام الكلية والمهام الصغيرة في js
.
في js
، يتم تقسيم المهام إلى مهام متزامنة ومهام غير متزامنة، فما هي المهام المتزامنة وما هي المهام غير المتزامنة؟
تشير المهام المتزامنة إلى المهام الموضوعة في قائمة الانتظار للتنفيذ على مؤشر الترابط الرئيسي. لا يمكن تنفيذ المهمة التالية إلا بعد تنفيذ المهمة السابقة.
تشير المهام غير المتزامنة إلى المهام التي لا تدخل إلى مؤشر الترابط الرئيسي ولكنها تدخل "قائمة انتظار المهام" (يتم تنفيذ المهام الموجودة في قائمة انتظار المهام بالتوازي مع مؤشر الترابط الرئيسي فقط عندما يكون مؤشر الترابط الرئيسي خاملاً وتقوم "قائمة انتظار المهام" بإعلام الخيط الرئيسي، مهمة غير متزامنة بمجرد تنفيذها، ستدخل المهمة إلى الخيط الرئيسي للتنفيذ. نظرًا لأنه مخزن في قائمة الانتظار، فإنه يلبي قاعدة ما يدخل أولاً يخرج أولاً . تتضمن المهام غير المتزامنة الشائعة setInterval
و setTimeout
و promise.then
وما إلى ذلك.
سابقًا المهام المتزامنة والمهام غير المتزامنة. الآن دعونا نتحدث عن حلقة الحدث.
تدخل المهام المتزامنة وغير المتزامنة "أماكن" تنفيذ مختلفة على التوالي، وتدخل مؤشر الترابط الرئيسي بشكل متزامن فقط عند اكتمال المهمة السابقة، يمكن تنفيذ المهمة التالية. المهام غير المتزامنة لا تدخل إلى الموضوع الرئيسي ولكنها تدخل إلى Event Table
وتسجل الوظائف.
عند اكتمال الشيء المحدد، سيقوم Event Table
بنقل هذه الوظيفة إلى Event Queue
. Event Queue
عبارة عن بنية بيانات لقائمة الانتظار، لذا فهي تلبي قاعدة ما يدخل أولاً يخرج أولاً.
عندما تكون المهام في الموضوع الرئيسي فارغة بعد التنفيذ، ستتم قراءة الوظيفة المقابلة من Event Queue
وتنفيذها في الموضوع الرئيسي.
سيتم تكرار العملية المذكورة أعلاه بشكل مستمر، وهو ما يسمى غالبًا بحلقة الأحداث .
دعونا نلخص ذلك مع الصورة
اسمحوا لي أن أقدم بإيجاز مثالًا
على وظيفة test1() { console.log("log1"); setTimeout(() => { console.log("setTimeout 1000"); }, 1000); setTimeout(() => { console.log("setTimeout 100"); }, 100); console.log("log2"); } test1(); // log1, log2, setTimeout 100, setTimeout 1000
نحن نعلم أنه في js، سيتم تنفيذ المهام المتزامنة أولاً قبل المهام غير المتزامنة، لذلك سيخرج المثال أعلاه log1、log2
أولاً،
ثم ينفذ المهام غير المتزامنة بعد المهام المتزامنة. لذلك، ستنفذ وظيفة رد الاتصال بتأخير قدره 100
مللي ثانية إخراج setTimeout 100
أولاً،
وستقوم وظيفة رد الاتصال بتأخير قدره 1000
مللي ثانية بتنفيذ إخراج setTimeout 1000
لاحقًا طالما أنك تفهم المهام المتزامنة وغير المتزامنة التي ذكرها المؤلف أعلاه، فلن تكون هناك مشكلة. ثم اسمحوا لي أن أقدم لكم مثالا آخر، دعونا نرى ما ستكون النتيجة.
اختبار الوظيفة 2 () { console.log("log1"); setTimeout(() => { console.log("setTimeout 1000"); }, 1000); setTimeout(() => { console.log("setTimeout 100"); }, 100); وعد جديد((حل، رفض) => { console.log("الوعد الجديد"); حل()؛ }).ثم(() => { console.log("promise.then"); }); console.log("log2"); } test2();
لحل المشكلة المذكورة أعلاه، لا يكفي معرفة المهام المتزامنة وغير المتزامنة، بل نحتاج أيضًا إلى معرفة المهام الكلية والمهام الصغيرة.
في js
، تنقسم المهام إلى نوعين، أحدهما يسمى مهمة الماكرو MacroTask
، والآخر يسمى المهمة الدقيقة MicroTask
.
تحتوي مهمة الماكرو الشائعة MacroTask
على
كتلة التعليمات البرمجية الرئيسية
setTimeout()
setInterval()
setImmediate() - Node
requestAnimationFrame() -
المهمة الصغيرة الشائعة لدى MicroTask
Promise.then()
process.nextTick() -
Node المثال أعلاه يتضمن مهام ماكرو ومهام صغيرة. ما هو ترتيب تنفيذ مهام الماكرو والمهام الصغيرة؟
بادئ ذي بدء، عندما يبدأ تنفيذ script
الشامل (كأول مهمة ماكرو)، سيتم تقسيم جميع التعليمات البرمجية إلى جزأين: المهام المتزامنة والمهام غير المتزامنة، وستدخل المهام المتزامنة مباشرة إلى الخيط الرئيسي للتنفيذ بالتسلسل. وستدخل المهام غير المتزامنة إلى قائمة الانتظار غير المتزامنة ثم يتم تقسيمها إلى مهام ماكرو ومهام صغيرة.
تدخل مهمة الماكرو Event Table
وتسجل وظيفة رد اتصال فيه. عند اكتمال الحدث المحدد، سينقل Event Table
هذه Event Queue
إلى قائمة انتظار الأحداث. وستدخل المهمة الصغيرة أيضًا إلى Event Table
آخر وتسجل فيه عند اكتمال الحدث المحدد، سينقل Event Table
هذه الوظيفة إلى Event Queue
عند اكتمال المهام في سلسلة المحادثات الرئيسية ويكون مؤشر الترابط الرئيسي فارغًا، سيتم التحقق من Event Queue
الخاصة بالمهمة الدقيقة ، قم بتنفيذ كل شيء، إذا لم يكن الأمر كذلك، فقم بتنفيذ مهمة الماكرو التالية.
نستخدم صورة لتلخيصها.
بعد فهم الأمثلة المذكورة أعلاه للمهام الكلية والمهام الصغيرة غير المتزامنة، يمكننا الحصول على الإجابة بسهولة.
نحن نعلم أنه في JS، سيتم تنفيذ المهام المتزامنة أولاً قبل المهام غير المتزامنة، وبالتالي فإن المثال أعلاه سيخرج log1、new promise、log2
أولاً. تجدر الإشارة هنا إلى أنه تتم مزامنة كتلة التعليمات البرمجية الرئيسية للوعد الجديد
بعد تنفيذ مهمة الماكرو، وسيتم تنفيذ جميع المهام الصغيرة التي تم إنشاؤها بواسطة مهمة الماكرو هذه، لذلك سيتم إخراج promise.then
بعد تنفيذ جميع المهام الصغيرة ، سيتم تنفيذ مهمة ماكرو أخرى، وتأخير وظيفة رد الاتصال البالغة 100
مللي ثانية ستعطي الأولوية للتنفيذ والإخراج setTimeout 100
لا تنشئ هذه المهمة الكبيرة مهام صغيرة، لذلك لا توجد مهام صغيرة يلزم تنفيذها
لمواصلة تنفيذ المهمة الكبيرة التالية رد الاتصال ستعطي الوظيفة ذات التأخير 1000
الأولوية للتنفيذ والإخراج setTimeout 1000
لذلك بعد تنفيذ طريقة test2، سيتم إخراج log1、new promise、log2、promise.then、setTimeout 100、setTimeout 1000
في
مقالات متسلسلة على الإنترنت آراء مختلفة حول ما إذا كان سيتم تنفيذ
js
أولاً بمهام ماكرو ثم بمهام صغيرة أو بمهام صغيرة قبل مهام ماكرو. ما يفهمه المؤلف هو أنه إذا تم اعتبار كتلة كودjs
بأكملها مهمة ماكرو، فإن ترتيب تنفيذjs
الخاص بنا هو مهمة ماكرو أولاً ثم مهمة صغيرة.
كما يقول المثل، الممارسة مرة واحدة أفضل من المشاهدة مائة مرة، سأقدم لك مثالين أدناه، إذا تمكنت من القيام بذلك بشكل صحيح، فقد أتقنت معرفة آلية تنفيذ js
.
مثال 1
اختبار الوظيفة3() { console.log(1); setTimeout(وظيفة () { console.log(2); وعد جديد (وظيفة (حل) { console.log(3); حل()؛ }).ثم (وظيفة () { console.log(4); }); console.log(5); }, 1000); وعد جديد (وظيفة (حل) { console.log(6); حل()؛ }).ثم (وظيفة () { console.log(7); setTimeout(وظيفة () { console.log(8); }); }); setTimeout(وظيفة () { console.log(9); وعد جديد (وظيفة (حل) { console.log(10); حل()؛ }).ثم (وظيفة () { console.log(11); }); }, 100); console.log(12); } test3();
دعنا نحللها بالتفصيل
أولاً، يتم تنفيذ كتلة كود js
الشاملة كمهمة ماكرو، ويتم إخراج 1 و 1、6、12
بالتسلسل.
بعد تنفيذ مهمة الماكرو الشاملة لكتلة التعليمات البرمجية، يتم إنشاء مهمة صغيرة واحدة ومهمتين ماكرو، وبالتالي فإن قائمة انتظار مهام الماكرو تحتوي على مهمتين ماكرو وقائمة انتظار المهام الصغيرة تحتوي على مهمة صغيرة واحدة.
بعد تنفيذ مهمة الماكرو، سيتم تنفيذ جميع المهام الصغيرة التي تم إنشاؤها بواسطة مهمة الماكرو هذه. نظرًا لوجود مهمة صغيرة واحدة فقط، سيتم إخراج 7
منها. أنتجت هذه المهمة الدقيقة مهمة كبيرة أخرى، لذا يوجد حاليًا ثلاث مهام كبيرة في قائمة انتظار المهام الكبيرة.
من بين المهام الكبيرة الثلاثة، يتم تنفيذ المهمة التي لم يتم تعيين تأخير فيها أولاً، لذلك يتم إخراج 8
لا تنشئ هذه المهمة الكبيرة مهام صغيرة، لذلك لا توجد مهام صغيرة للتنفيذ، ويستمر تنفيذ المهمة الكبيرة التالية.
قم بتأخير تنفيذ المهام الصغيرة لمدة 100
مللي ثانية، وإخراج 9、10
، وإنشاء مهمة صغيرة، بحيث تحتوي قائمة انتظار المهام الصغيرة حاليًا على مهمة صغيرة
بعد تنفيذ المهمة الكبيرة، سيتم تنفيذ جميع المهام الدقيقة التي تم إنشاؤها بواسطة المهمة الكبيرة، لذلك سيتم تنفيذ المهمة الدقيقة. تم تنفيذ جميع المهام الدقيقة في إخراج قائمة انتظار المهام 11
يُخرج تنفيذ المهام الدقيقة 2、3、5
بتأخير قدره 1000
مللي ثانية، وبالتالي يتم إنشاء مهمة صغيرة، تحتوي قائمة انتظار المهام الدقيقة حاليًا على مهمة صغيرة
. سيتم تنفيذ المهام الكبيرة. سيتم إنشاء جميع المهام الدقيقة، لذلك سيتم تنفيذ جميع المهام الدقيقة في قائمة انتظار المهام الدقيقة، وسيتم إخراج 4
، لذلك سيخرج مثال الكود أعلاه 1、6、12、7、8、9、10、11、2、3、5、4
، 12، 7، 8، 9، 10، 11. ، 2، 3، 5، 4 بالتسلسل هل فعلتم ذلك بشكل صحيح يا رفاق؟
المثال 2:
قمنا بتعديل المثال 1 أعلاه بشكل طفيف ونقدم async
await
دالة غير متزامنة test4() { console.log(1); setTimeout(وظيفة () { console.log(2); وعد جديد (وظيفة (حل) { console.log(3); حل()؛ }).ثم (وظيفة () { console.log(4); }); console.log(5); }, 1000); وعد جديد (وظيفة (حل) { console.log(6); حل()؛ }).ثم (وظيفة () { console.log(7); setTimeout(وظيفة () { console.log(8); }); }); نتيجة const = انتظار async1(); console.log(result); setTimeout(وظيفة () { console.log(9); وعد جديد (وظيفة (حل) { console.log(10); حل()؛ }).ثم (وظيفة () { console.log(11); }); }, 100); console.log(12); } وظيفة غير متزامنة async1() { console.log(13) إرجاع Promise.resolve("Promise.resolve"); } test4();
ما الذي سيخرجه المثال أعلاه؟ هنا يمكننا بسهولة حل مشكلة async
await
.
نحن نعلم async
و await
هما في الواقع عبارة عن سكر بناء جملة لـ Promise
. هنا نحتاج فقط إلى معرفة await
يعادل Promise.then
. حتى نتمكن من فهم المثال أعلاه كدالة التعليمات البرمجية التالية
test4() { console.log(1); setTimeout(وظيفة () { console.log(2); وعد جديد (وظيفة (حل) { console.log(3); حل()؛ }).ثم (وظيفة () { console.log(4); }); console.log(5); }, 1000); وعد جديد (وظيفة (حل) { console.log(6); حل()؛ }).ثم (وظيفة () { console.log(7); setTimeout(وظيفة () { console.log(8); }); }); وعد جديد (وظيفة (حل) { console.log(13); إرجاع العزم("Promise.resolve"); }).ثم((النتيجة) => { console.log(result); setTimeout(وظيفة () { console.log(9); وعد جديد (وظيفة (حل) { console.log(10); حل()؛ }).ثم (وظيفة () { console.log(11); }); }, 100); console.log(12); }); } test4();
هل يمكنك بسهولة الحصول على النتيجة بعد رؤية الكود أعلاه؟
أولاً، يتم تنفيذ كتلة كود js
بأكملها في البداية كمهمة ماكرو، ويتم إخراج 1、6、13
بالتسلسل.
بعد تنفيذ مهمة الماكرو الشاملة لكتلة التعليمات البرمجية، يتم إنشاء مهمتين صغيرتين ومهمة ماكرو واحدة، وبالتالي فإن قائمة انتظار مهام الماكرو تحتوي على مهمة ماكرو واحدة وقائمة انتظار المهام الصغيرة تحتوي على مهمتين صغيرتين.
بعد تنفيذ مهمة الماكرو، سيتم تنفيذ جميع المهام الصغيرة التي تم إنشاؤها بواسطة مهمة الماكرو هذه. إذن 7、Promise.resolve、12
. أنتجت هذه المهمة الدقيقة مهمتين كبيرتين أخريين، وبالتالي فإن قائمة انتظار المهام الكبيرة تحتوي حاليًا على ثلاث مهام كبيرة.
من بين المهام الكبيرة الثلاثة، يتم تنفيذ المهمة التي لم يتم تعيين تأخير فيها أولاً، لذلك يتم إخراج 8
لا تنشئ هذه المهمة الكبيرة مهام صغيرة، لذلك لا توجد مهام صغيرة للتنفيذ، ويستمر تنفيذ المهمة الكبيرة التالية.
قم بتأخير تنفيذ المهام الصغيرة لمدة 100
مللي ثانية، وإخراج 9、10
، وإنشاء مهمة صغيرة، بحيث تحتوي قائمة انتظار المهام الصغيرة حاليًا على مهمة صغيرة
بعد تنفيذ المهمة الكبيرة، سيتم تنفيذ جميع المهام الدقيقة التي تم إنشاؤها بواسطة المهمة الكبيرة، لذلك سيتم تنفيذ المهمة الدقيقة. تم تنفيذ جميع المهام الدقيقة في إخراج قائمة انتظار المهام 11
يُخرج تنفيذ المهام الدقيقة 2、3、5
بتأخير قدره 1000
مللي ثانية، وبالتالي يتم إنشاء مهمة صغيرة، تحتوي قائمة انتظار المهام الدقيقة حاليًا على مهمة صغيرة
. سيتم تنفيذ المهام الكبيرة. جميع المهام الدقيقة التي تم إنشاؤها ستنفذ جميع المهام الدقيقة في قائمة انتظار المهام الصغيرة والإخراج 4
لذلك، سيخرج مثال التعليمات البرمجية أعلاه 1، 6، 13، 7 1、6、13、7、Promise.resolve、12、8、9、10、11、2、3、5、4
4، هل فعلتم ذلك بشكل صحيح يا رفاق؟
ربما لا يزال العديد من الأصدقاء لا يفهمون setTimeout(fn)
أليس من الواضح أنه لم يتم تعيين وقت التأخير؟ ألا ينبغي تنفيذه على الفور؟
يمكننا أن نفهم setTimeout(fn)
على أنه setTimeout(fn,0)
، وهو ما يعني في الواقع نفس الشيء.
نحن نعلم أن js مقسمة إلى مهام متزامنة ومهام غير متزامنة. setTimeout(fn)
هي مهمة غير متزامنة، لذلك حتى إذا لم تقم بتعيين وقت تأخير هنا، فسوف تدخل في قائمة الانتظار غير المتزامنة ولن يتم تنفيذها حتى يتم تنفيذ مؤشر الترابط الرئيسي. عاطل.
سيذكر المؤلف ذلك مرة أخرى، هل تعتقد أن وقت التأخير الذي حددناه بعد setTimeout
، سيتم تنفيذ js
بالتأكيد وفقًا لوقت التأخير لدينا، لا أعتقد ذلك. الوقت الذي حددناه هو فقط إمكانية تنفيذ وظيفة رد الاتصال، ولكن ما إذا كان الخيط الرئيسي مجانيًا هو أمر آخر يمكننا تقديم مثال بسيط عليه.
اختبار الوظيفة5() { setTimeout(وظيفة () { console.log("setTimeout"); }, 100); دعني = 0؛ بينما (صحيح) { أنا++; } } test5();
هل سيخرج المثال أعلاه بالتأكيد setTimeout
بعد 100
مللي ثانية؟ لا، لأن مؤشر الترابط الرئيسي لدينا دخل في حلقة لا نهائية وليس لديه وقت لتنفيذ مهام قائمة الانتظار غير المتزامنة.
تم ذكر GUI渲染
الرسومية هنا، وقد لا يفهمه بعض الأصدقاء، وسأقدمه بالتفصيل في مقال حول المتصفحات فيما يلي مجرد فهم موجز.
نظرًا لأن JS引擎线程
GUI渲染线程
متنافيان، من أجل تمكين宏任务
DOM任务
من المتابعة بطريقة منظمة، سيبدأ المتصفح GUI渲染线程
بعد نتيجة تنفيذ宏任务
واحدة وقبل تنفيذ宏任务
التالية، تقديم الصفحة.
لذلك، فإن العلاقة بين مهام الماكرو والمهام الصغيرة وعرض واجهة المستخدم الرسومية هي كما يلي:
مهمة ماكرو -> مهمة صغيرة -> عرض واجهة المستخدم الرسومية -> مهمة ماكرو ->...
[توصية دروس الفيديو ذات الصلة: الواجهة الأمامية للويب]
ما ورد أعلاه هو تحليل متعمق لجافا سكريبت للحصول على تفاصيل حول سياق التنفيذ وآلية التنفيذ، يرجى الانتباه إلى المقالات الأخرى ذات الصلة على موقع PHP الصيني لمزيد من المعلومات!