نقش تایمر های جاوا اسکریپت در ایونت لوپ Nodejs

تایمر های جاوا اسکریپت چه نقشی در Event Loop Nodejs دارند؟ مثلا process.nextTick() قسمت مهمی از آن محسوب میشود که در این مقاله (ترجمه از دایکومنتیشن سایت رسمی ند جی اس) میخوانیم.

تایمر های جاوا اسکریپت: process.nextTick

هربار که Event Loop یک دوره کامل را طی میکند tick گفته میشود. وقتی ما تابعی را داخل process.nextTick() قرار میدهیم، به موتور تایمر های جاوا اسکریپت دستور میدهیم این تابع را در پایان اجرای کنونی درست قبل از شروع tick بعدی (در ایونت لوپ)، فراخوانی کند.

process.nextTick(() => {
  //do something
})

Event Loop مشغول پردازش کدهای فعلی است.

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

این راه حلی برای اجرای ناهمزمان async در موتور جاوا اسکریپت است. به این ترتیب موتور جاوااسکریپت (بعد از فانکشن کنونی) به محض اینکه فرصتی پیدا کند (بدون اینکه در صف قرار دهد) اجرای ناهمزمان انجام میدهد.

کدهای setTimeout(() => {}, 0) کدهای تابع کنونی را در پایان tick بعدی اجرا میکند. زمانی خیلی بیشتر از nextTick() که فراخوانی را زمان بندی میکند و درست قبل از شروع تیک بعدی اجرا میکند.

از nextTick() وقتی استفاده کنید که میخواهید مطمئن شوید قبل از اجرای حلقه بعدی کدهایتان اجرا شده اند.

درک مفهوم setImmediate

قابلیت ناهمزمانی یا همان async با setTimeout ممکن بود. اما اگر زودتر بخواهیم اجرا شود (در اسرع وقت) از setImmediate استفاده میکنیم. این تابع که از تایمر های جاوا اسکریپت محسوب میشود، فقط در Node قابل استفاده است.

setImmediate(() => {
  //run something
})

هر تابعی که به عنوان آرگومان وارد setImmediate شود ، یک callback است که در تکرار بعدی ایونت لوپ اجرا خواهد شد.

تابع setImmediate() با setTimeout(() => {}, 0) و process.nextTick() چه فرقی دارد؟

تابعی که در process.nextTick() کال بک میشود در حلقه کنونی ایونت لوپ اجرا میشود. در اسرع وقت! پس از اجرای پردازش کنونی! بنابراین همواره زودتر از دو تایمر setTimeout و setImmediate اجرا خواهد شد.

دو کال بک setTimeout(() => {}, 0) و setImmediate مشابه هم عمل میکنند و هر دو در تکرار بعدی حلقه اجرا خواهند شد.

آشنایی با JavaScript Timers

وقتی کدهای جاوااسکریپت مینویسید ممکن است بخواهید یک تابع را بعدا به اجرا درآورید.

این کار setTimeout است. شما یک کال بک فانکشن را در آن به کار میبرید تا با تاخیر اجرا شود. مقداری به آن میدهید تا با همان میلی ثانیه تاخیر اجرا شود.

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

setTimeout(() => {
  // runs after 50 milliseconds
}, 50)

این قاعده نوشتاری، یک تابع جدید تعریف میکند. شما میتوانید هر تابع دیگری را هم که از قبل تعریف کرده اید اینجا کال بک کنید! به مثال زیر توجه کنید:

const myFunction = (firstParam, secondParam) => {
  // do something
}

// runs after 2 seconds
setTimeout(myFunction, 2000, firstParam, secondParam)

آیا میدانید setTimeout یک id برمیگرداند؟ این id عموما کاربردی ندارد. فقط برای کنسل کردن این اجرای زمان دار کال بک فانکشن قابل استفاده است.

const id = setTimeout(() => {
  // should run after 2 seconds
}, 2000)

// I changed my mind
clearTimeout(id)

بدون تاخیر Zero Delay

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

setTimeout(() => {
  console.log('after ')
}, 0)

console.log(' before ')

خروجی این کدها before after خواهد بود. چرا اول before نوشته شد؟

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

برخی از مرورگرها (IE و Edge) متد () setImmediate را پیاده سازی می کنند. اما سایر مرورگرها این متد را ندارند. اما در Nodejs این متد استاندارد و در دسترس است.

تایمر های جاوا اسکریپت : setInterval

setInterval یک تابع شبیه setTimeout است با این تفاوت که بصورت مداوم یک کال بک را تکرار میکند. (بجای اینکه فقط یکبار ان را اجرا کند.)

setInterval(() => {
  // runs every 2 seconds
}, 2000)

تابع بالا هر دو ثانیه یکبار اجرا میشود. مگر اینکه شما با clearInterval آن را متوقف کنید:

const id = setInterval(() => {
  // runs every 2 seconds
}, 2000)

clearInterval(id)

این کار رایج است که در داخل کال بک فانکشن از clearInterval استفاده شود. با این کار کال بک فانکشن ما بصورت اتوماتیک تصمیم میگیرد که چه زمانی باید این فرایند ادامه یابد یا متوقف شود. مثلا تابع زیر همواره اجرا میشود تا زمانیکه مقدار App.somethingIWait برابر با arrived شود:

const interval = setInterval(() => {
  if (App.somethingIWait === 'arrived') {
    clearInterval(interval)
    return
  }
  // otherwise do things
}, 100)

مشکل setTimeout : روی هم افتادن زمان اجرای کال بک ها

setInterval یک تابع را هر n میلی ثانیه اجرا میکند. بدون اینکه توجه کند این تابع در چه زمانی اجرایش تمام میشود.

وقتی یک تابع زمان اجرایش همواره ثابت باشد، همه چیز خوب پیش میرود:

وقتی زمان اجرای تابع همیشه یک مقدار ثابت نباشد: (مثلا بخاطر شرایط مختلف شبکه)

حتی ممکن است زمان اجرا روی زمان اجرای دومین بار را هم بگیرد:

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

const myFunction = () => {
  // do something

  setTimeout(myFunction, 1000)
}

setTimeout(myFunction, 1000)

setTimeout و setInterval از طریق Timers module در nodejs در دسترس هستند.

ند جی اس همچنین setImmediate() را ارائه میدهد که معادل setTimeout(() => {}, 0) است. در هنگام کار با ایونت لوپ ها به کار می آید.

اگر میخواهید مقاله ترجمه شده ی دیگری درباره ایونت لوپ بخوانید، این مقاله را از دست ندهید!