حلقة حدث JavaScript

تعد Event Loop أحد أهم الجوانب التي يجب فهمها حول JavaScript. هذا المنشور يشرح ذلك بعبارات بسيطة

مقدمة

الحلقة الحدثأحد أهم الجوانب التي يجب فهمها حول JavaScript

لقد برمجت لسنوات باستخدام JavaScript ، ولكني لم أقم بذلك مطلقًاتمامافهم كيف تعمل الأشياء تحت الغطاء. من الجيد تمامًا عدم معرفة هذا المفهوم بالتفصيل ، ولكن كالمعتاد ، من المفيد معرفة كيفية عمله ، وقد تشعر بالفضول قليلاً في هذه المرحلة.

يهدف هذا المنشور إلى شرح التفاصيل الداخلية لكيفية عمل JavaScript مع خيط واحد ، وكيف يتعامل مع الوظائف غير المتزامنة.

يتم تشغيل كود JavaScript الخاص بك في سلسلة واحدة هناك شيء واحد فقط يحدث في كل مرة.

هذا قيد مفيد جدًا في الواقع ، لأنه يبسط كثيرًا من كيفية البرمجة دون القلق بشأن مشكلات التزامن.

ما عليك سوى الانتباه إلى كيفية كتابة التعليمات البرمجية الخاصة بك وتجنب أي شيء يمكن أن يحجب الخيط ، مثل مكالمات الشبكة المتزامنة أو اللانهائيةالحلقات.

بشكل عام ، توجد في معظم المتصفحات حلقة حدث لكل علامة تبويب في المتصفح ، وذلك لجعل كل عملية معزولة وتجنب صفحة الويب ذات الحلقات اللانهائية أو المعالجة الثقيلة لحظر المتصفح بأكمله.

تدير البيئة العديد من حلقات الأحداث المتزامنة ، للتعامل مع مكالمات API على سبيل المثال.عمال الويبتشغيل في حلقة الحدث الخاصة بهم أيضًا.

أنت في حاجة إلى القلق من ذلككودكسيتم تشغيله في حلقة حدث واحد ، واكتب رمزًا مع وضع هذا الشيء في الاعتبار لتجنب حظره.

منع حلقة الحدث

أي كود JavaScript يستغرق وقتًا طويلاً لإعادة التحكم مرة أخرى إلى حلقة الحدث سيحظر تنفيذ أي كود JavaScript في الصفحة ، حتى أنه يحظر مؤشر ترابط واجهة المستخدم ، ولا يمكن للمستخدم النقر فوقها ، وتمرير الصفحة ، وما إلى ذلك.

تقريبًا جميع أساسيات الإدخال / الإخراج في JavaScript غير محظورة. طلبات الشبكة ،Node.jsعمليات نظام الملفات ، وما إلى ذلك. الاستثناء هو الحظر ، وهذا هو السبب في أن JavaScript يعتمد كثيرًا على عمليات الاسترجاعات ومؤخرًاوعودوغير متزامن / انتظار.

مكدس المكالمات

مكدس الاستدعاءات هو قائمة انتظار LIFO (Last In، First Out)

تفحص حلقة الحدث باستمرارمكدس المكالماتلمعرفة ما إذا كان هناك أي وظيفة تحتاج إلى تشغيل.

أثناء القيام بذلك ، فإنه يضيف أي استدعاء دالة يجده إلى مكدس الاستدعاءات وينفذ كل واحد بالترتيب.

هل تعرف تتبع مكدس الأخطاء الذي قد تكون معتادًا عليه ، في مصحح الأخطاء أو في وحدة تحكم المستعرض؟ يبحث المستعرض عن أسماء الوظائف في مكدس الاستدعاءات لإعلامك بالوظيفة التي تنشئ الاستدعاء الحالي:

Exception call stack

شرح حلقة حدث بسيط

دعنا نختار مثالا:

أنا أستعملfooوbarوbazمثلأسماء عشوائية. أدخل أي نوع من الأسماء لاستبدالها

const bar = () => console.log('bar')

const baz = () => console.log(‘baz’)

const foo = () => { console.log(‘foo’) bar() baz() }

foo()

تتم طباعة هذا الرمز

foo
bar
baz

as expected.

When this code runs, first foo() is called. Inside foo() we first call bar(), then we call baz().

At this point the call stack looks like this:

Call stack first example

The event loop on every iteration looks if there’s something in the call stack, and executes it:

Execution order first example

until the call stack is empty.

Queuing function execution

The above example looks normal, there’s nothing special about it: JavaScript finds things to execute, runs them in order.

Let’s see how to defer a function until the stack is clear.

The use case of setTimeout(() => {}), 0) is to call a function, but execute it once every other function in the code has executed.

Take this example:

const bar = () => console.log('bar')

const baz = () => console.log(‘baz’)

const foo = () => { console.log(‘foo’) setTimeout(bar, 0) baz() }

foo()

This code prints, maybe surprisingly:

foo
baz
bar

When this code runs, first foo() is called. Inside foo() we first call setTimeout, passing bar as an argument, and we instruct it to run immediately as fast as it can, passing 0 as the timer. Then we call baz().

At this point the call stack looks like this:

Call stack second example

Here is the execution order for all the functions in our program:

Execution order second example

Why is this happening?

The Message Queue

When setTimeout() is called, the Browser or Node.js start the timer. Once the timer expires, in this case immediately as we put 0 as the timeout, the callback function is put in the Message Queue.

The Message Queue is also where user-initiated events like click or keyboard events, or fetch responses are queued before your code has the opportunity to react to them. Or also DOM events like onLoad.

The loop gives priority to the call stack, and it first processes everything it finds in the call stack, and once there’s nothing in there, it goes to pick up things in the message queue.

We don’t have to wait for functions like setTimeout, fetch or other things to do their own work, because they are provided by the browser, and they live on their own threads. For example, if you set the setTimeout timeout to 2 seconds, you don’t have to wait 2 seconds - the wait happens elsewhere.

ES6 Job Queue

ECMAScript 2015 introduced the concept of the Job Queue, which is used by Promises (also introduced in ES6/ES2015). It’s a way to execute the result of an async function as soon as possible, rather than being put at the end of the call stack.

Promises that resolve before the current function ends will be executed right after the current function.

I find nice the analogy of a rollercoaster ride at an amusement park: the message queue puts you at the back of the queue, behind all the other people, where you will have to wait for your turn, while the job queue is the fastpass ticket that lets you take another ride right after you finished the previous one.

Example:

const bar = () => console.log('bar')

const baz = () => console.log(‘baz’)

const foo = () => { console.log(‘foo’) setTimeout(bar, 0) new Promise((resolve, reject) => resolve(‘should be right after baz, before bar’) ).then(resolve => console.log(resolve)) baz() }

foo()

This prints

foo
baz
should be right after baz, before bar
bar

That’s a big difference between Promises (and Async/await, which is built on promises) and plain old asynchronous functions through setTimeout() or other platform APIs.


More js tutorials: