مقدمه

جاوااسکریپت ذاتاً یک زبان تک‌ریسمانی (single-threaded) است، یعنی در هر لحظه فقط یک کار را می‌تواند انجام دهد. با این حال، با کمک رویدادها (event)، تایمرها و APIهای غیرهمزمان (asynchronous)، می‌توان چند عملیات را به‌صورت موازی مدیریت کرد تا کارایی، واکنش‌پذیری و تجربه کاربری بهبود پیدا کند. در مدل synchronous (همزمان)، همه دستورات پشت سر هم اجرا می‌شوند و هر کدام باید منتظر اتمام قبلی بماند. اما در مدل asynchronous (غیرهمزمان)، می‌توان وظایفی را به آینده موکول کرد تا کد منتظر نشود و اجرای برنامه متوقف نشود.

چرخه رویداد (Event Loop) و مفهوم Callback

قلب برنامه‌نویسی غیرهمزمان در جاوااسکریپت، چرخه رویداد (event loop) است. این سازوکار به کد اجازه می‌دهد عملیات طولانی مثل دانلود، خواندن فایل یا درخواست به سرور را بدون قفل کردن کل برنامه انجام دهد و بعد از پایان، ادامه کار را با callback یا promise اجرا کند.

یادآوری می‌کنم که در جاوااسکریپت، callback یک تابع است که به عنوان آرگومان به تابع دیگر داده می‌شود تا پس از اتمام یک عملیات اجرا شود. تا قبل از معرفی Promise و الگوی async/await از توابع callback برای پیاده‌سازی کدنویسی غیرهمزمان استفاده می‌شد. مثال زیر را ببینید.

Copy Icon JAVASCRIPT
console.log("start");
setTimeout(function() {
  console.log("one second after");
}, 1000);
console.log("end");

در اینجا ماهیت غیرهمزمان تابع setTimeout باعث می‌شود پیام "end" قبل از پیام "one second after" نمایش داده شود، چون عملیات غیرهمزمان است و منتظر اتمام آن نمی‌ماند.

توابعی مثل setTimeout و setInterval و عملیات‌هایی مانند درخواست AJAX یا خواندن فایل، همگی به صورت غیرهمزمان اجرا می‌شوند و نتیجه آن‌ها بعداً (در صف رویداد) به کد شما بازمی‌گردد.

توضیح بیشتر در مورد چرخه رویداد

چرخه رویداد (Event Loop) در جاوااسکریپت وظیفه دارد که اجرای کد، جمع‌آوری رویدادها و اجرای توابع صف شده (callback queue) را مدیریت کند. زمانی که کد اصلی (call stack) خالی شد، چرخه رویداد callbackهای آماده را یکی یکی اجرا می‌کند.

  • کدهای همزمان (synchronous) مستقیماً اجرا می‌شوند.
  • کدهای غیرهمزمان (asynchronous) مانند setTimeout یا درخواست AJAX ابتدا به Web APIs ارسال می‌شوند و پس از اتمام، callback آن‌ها وارد صف رویداد (callback queue) می‌شود.
  • Event Loop بررسی می‌کند که call stack خالی است یا نه. اگر خالی بود، اولین callback از صف رویداد را اجرا می‌کند.

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

چرخه رویداد در جاوااسکریپت
نمایی شماتیک از چرخه رویداد در جاوااسکریپت

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

مشکل Callback Hell و نیاز به راهکار بهتر

وقتی چند عملیات غیرهمزمان پشت سر هم نیاز به اجرا دارند، باید هر کدام در callback بعدی قرار بگیرند که باعث تو در تو شدن و سخت شدن مدیریت کد می‌شود. مثال زیر را ببینید.

Copy Icon JAVASCRIPT
doSomething(function(result1) {
  doAnother(result1, function(result2) {
    doLast(result2, function(result3) {
      console.log(result3);
    });
  });
});

این وضعیت که از آن با عنوان Callback Hell یا جهنم توابع callback یاد می‌شود، کدها را ناخوانا می‌‌کند و مدیریت آنها را سخت می‌کند. Promise راه‌حلی برای مدیریت ساده‌تر و حرفه‌ای‌تر عملیات غیرهمزمان است. در درس بعد به بررسی این مفهوم می‌پردازیم.