Event Loop ها در جاوااسکریپت

Event Loop ها در جاوااسکریپت

Event Loop ها در جاوااسکریپت

The JavaScript Event Loop

توسط : admin
حلقه رویداد یکی از مهمترین جنبه های درک در مورد جاوا اسکریپت است. این پست آن را به زبان ساده توضیح می دهد .

 

مقدمه 

Event Loop یا حلقه رویداد یکی از مهمترین جنبه های درک در مورد جاوا اسکریپت است.

من سالها با جاوا اسکریپت برنامه نویسی کرده ام ، اما هرگز درک نکرده ام که چگونه عملیات در پشت پرده چگونه انجام میشود . کاملاً طبیعی است که این مفهوم را با جزئیات نشناسید ، اما طبق معمول ، مفید است که بدانید چگونه کار می کند ، و همچنین ممکن است که دانستن این موضوع کنجکاوی شما را بر انگیخته باشد .

 

این پست با هدف توضیح جزئیات درونی نحوه عملکرد جاوا اسکریپت به صورت تک رویداده یا single thread  و نحوه عملکرد توابع ناهمزمان انجام شده است.

کد JavaScript شما به صورت تک رشته ای اجرا می شود. در هر زمان فقط یک رویداد انجام میشود .

این محدودیتی است که در واقع بسیار مفید است ، زیرا نحوه برنامه ریزی شما را بدون نگرانی در مورد مشکلات همزمانی بسیار ساده می کند.

شما فقط باید به نحوه نوشتن کد خود توجه کنید و از هر چیزی که می تواند موضوع را مسدود کند ، مانند تماس های شبکه همزمان یا حلقه های نامحدود ، خودداری کنید.

به طور کلی ، در اکثر مرورگرها یک حلقه رویداد برای هر برگه مرورگر وجود دارد که می تواند هر فرآیند را جدا کرده و از یک صفحه وب با حلقه های نامحدود یا پردازش های سنگین جلوگیری کند تا کل مرورگر شما مسدود نشود .

 

این محیط چندین حلقه رویداد همزمان را مدیریت می کند ، برای مثال به درخواست های API رسیدگی می کند. کارگران وب در حلقه رویداد خود نیز اجرا را انجام میدهند .

 

شما به طور عمده باید نگران باشید که کد شما بر روی یک حلقه یک رویداد اجرا شود ، و برای جلوگیری از مسدود کردن کد ، کد را با توجه به این موضوع بنویسید.

 

مسدود کردن حلقه رویداد

هر کد JavaScript که زمان اجرای آن طولانی شده باشد،  برای بازگشت مجدد به حلقه رویداد  ، اجرای هر کد JavaScript در صفحه را مسدود می کند ، حتی  UI را  نیز مسدود می کند ، و کاربر نمی تواند در اطراف آن کلیک کند ، صفحه را حرکت دهد و غیره.

 

تقریباً تمام  I / O در JavaScript غیر مسدود شونده هستند. درخواست های درون شبکه ، عملیات سیستم فایل Node.js و غیره. مسدود کردن یک استثناست ، و به همین دلیل است که جاوا اسکریپت آنقدر مبتنی بر بازگشت به تماس ها ، و اخیراً بر روی promises ها و async / await است.

 

call stack

call stack  یک  صف LIFO است (آخرین ورودی ، اولین خروجی).

 

حلقه رویداد بطور مداوم call stack را بررسی می کند تا ببیند آیا عملکردی که باید اجرا شود وجود دارد یا خیر.

در ضمن انجام این کار ، هرگونه عملکردی را که می یابد به call stack اضافه می کند و هر یک را به ترتیب اجرا می کند.

آیا می دانید رد پشته خطایی که ممکن است با آن آشنا شوید ، در اشکال زدایی یا در کنسول مرورگر است؟ مرورگر نام عملکرد را در پشته فراخوانی جستجو می کند تا به شما اطلاع دهد که کدام عملکرد منشأ فراخوانی فعلی است:

Exception call stack

توضیح  ساده event loop 

مثال زیر را ببینید :

من از foo ، bar و baz به عنوان اسامی تصادفی استفاده می کنم. هر نوع نام را  میتوانید برای جایگزینی آنها وارد کنید .

 

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

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

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

foo()

 

خروجی این کد :

foo
bar
baz

همانطور که انتظار میرفت.

وقتی این کد اجرا شد ، ابتدا foo () خوانده می شود. درون foo () ابتدا bar ()  فراخوانی شده ، سپس baz ()  را فرا می خوانیم .

 

در این مرحله ، call stack به شرح زیر است:

Call stack first example

 

حلقه این رویداد در هر تکرار هر چیزی در پشته تماس وجود دارد ، آن را اجرا می کند:

Execution order first example

اجرا تا خالی شدن پشته ادامه می یابد .

 

اجرای صف توابع

مثال فوق طبیعی به نظر می رسد ، چیز خاصی در این مورد وجود ندارد: JavaScript چیزهایی را برای اجرای آن می یابد ، آنها را به ترتیب اجرا می کند.

 

بیایید ببینیم چگونه یک عملکرد را تا زمانی که پشته فعال نباشد ، به تعویق بیاندازیم .

 

استفاده از setTimeout (() => {}) ، 0) برای فراخوانی یک تابع است ، اما هر بار که عملکرد دیگری را در کد اجرا کرده است ، آن را اجرا میکند .

این مثال را بگیرید:

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

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

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

foo()

به طور شگفت انگیزیخروجی کد بالا به صورت زیر است :

foo
baz
bar

وقتی این کد اجرا شد ، ابتدا foo () خوانده می شود. در داخل foo () ما ابتدا با setTimeout  را فراخواندیم ، bar را به عنوان یک آرگومان پاس می دهیم ، و به آن دستور می دهیم که سریعاً  اجرا شود ، با گذشت زمان ۰ ثانیه به عنوان تایمر. سپس  baz () اجرا میشود .

 

در این مرحله ، پشته تماس به شرح زیر است:

 

Call stack second example

 

در اینجا دستور اجرای همه عملکردهای برنامه ما آمده است:

Execution order second example

چرا این اتفاق افتاد ؟

 صف پیام

هنگامی که setTimeout () فراخوانی می شود ، مرورگر یا Node.js تایمر را شروع می کنند. هنگامی که تایمر منقضی شد ، در این حالت بلافاصله وقتی 0 را به عنوان زمان اتمام قرار می دهیم ، عملکرد پاسخ به تماس در صف پیام قرار می گیرد.

 

صف پیام نیز در جایی قرار دارد که رویدادهای مبتنی بر کاربر مانند رویدادهای کلیک یا صفحه کلید یا پاسخهای واکشی در صف قبل از اینکه کد شما امکان واکنش به آنها را داشته باشد ، صف شود. یا همچنین رویدادهای DOM مانند onLoad .

 

حلقه رویداد ، اولویت را به پشته تماس یا call stack می دهد و ابتدا همه چیزهایی را که در پشته تماس یافت می شود پردازش می کند ، و هنگامی که هیچ چیزی در آنجا نباشد ، می رود چیزهایی را در صف پیام دریافت و اجرا کند.

 

ما لازم نیست برای انجام کارهای خود منتظر کارکردهایی مانند setTimeout ، واکشی یا موارد دیگر باشیم ، زیرا توسط مرورگر ارائه شده است ، و آنها روی موضوعات خودشان فعالیت می کنند. به عنوان مثال ، اگر زمان تنظیم setTimeout را به 2 ثانیه تنظیم کنید ، لازم نیست 2 ثانیه صبر کنید - انتظار در جای دیگر اتفاق می افتد.

 

ES6 Job Queue

ECMAScript 2015 مفهوم Job Queue را معرفی کرد ، که توسط Promise استفاده می شود (همچنین در ES6 / ES2015 معرفی شده است). این راهی برای اجرای نتیجه عملکرد aync در اسرع وقت است ، نه اینکه در انتهای پشته تماس قرار بگیرید.

Promise هایی که قبل از پایان عملکرد فعلی اجرا می شوند ، درست بعد از عملکرد فعلی اجرا می شوند.

قیاس بازی را در یک پارک تفریحی در نظر بگیرید . صف پیام ، شما را در پشت صف ، پشت سر همه افراد دیگر قرار می دهد ، جایی که شما باید منتظر نوبت خود باشید ، در حالی که صف کار بلیط  سریع است و این امکان را به شما می دهد که بلافاصله بعد از اتمام قبلی ، سوار اسباب بازی دیگری شوید.

 

مثال :

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()

خروجی این کد به این صورت است :

foo
baz
should be right after baz, before bar
bar

 

این یک تفاوت بزرگ بین Promise ها (و Async / await ، که با Promise ها ساخته شده است) و توابع ساده ناهمزمان قدیمی از طریق setTimeout () یا برنامه های دیگر بستر های نرم افزاری است.

 

برچسب ها

نظرات :

در عرض چند دقیقه برای ایجاد حساب

کاربری خود اقدام کنید


اکنون حساب کاربری خود را ایجاد کنید!


ایجاد حساب کاربری

با ثبت نام در نیلوتک از آخرین بروز رسانی های آموزش ها و مقالات سایت مطلع شوید