Synchronous و Asynchronous: دو مدل اجرای کد در جاوااسکریپت

جاوااسکریپت زبانی تک‌نخی (single-threaded) است؛ یعنی در هر لحظه فقط یک خط از کد در حال اجراست. با این حال، در طول این دوره بارها با کدهایی مواجه شده‌اید—مثل fetch، setTimeout و Promise—که به‌نوعی اجرای «هم‌زمان» چند عملیات را ممکن می‌کنند. راز این رفتار، تفاوت بنیادین میان دو مدل اجرای کد است: Synchronous (همزمان) و Asynchronous (ناهمزمان). درک درست این تفاوت، پیش‌نیاز اصلی برای فهم عمیق‌تر Promise، async/await و Event Loop در درس‌های بعدی این فصل است.

اجرای Synchronous: خط به خط و مسدودکننده

در مدل Synchronous، هر خط از کد باید کامل اجرا شود تا نوبت به خط بعدی برسد. اگر یک عملیات زمان‌بر باشد، کل برنامه تا پایان آن عملیات متوقف (block) می‌ماند:

console.log("مرحله‌ی اول");

function heavyCalculation() {
  let result = 0;
  for (let i = 0; i < 1000000000; i++) {
    result += i;
  }
  return result;
}

console.log("در حال محاسبه...");
heavyCalculation(); // این خط چند ثانیه طول می‌کشد و کل برنامه را متوقف می‌کند
console.log("مرحله‌ی سوم");

در این مثال، خط console.log("مرحله‌ی سوم") تا زمانی که heavyCalculation کاملاً تمام نشود، اجرا نمی‌شود. اگر این کد در مرورگر اجرا شود، در طول این مدت کل صفحه، از جمله کلیک‌ها و اسکرول، عملاً قفل خواهد بود.

اجرای Asynchronous: بدون مسدود کردن باقی کد

در مدل Asynchronous، یک عملیات زمان‌بر—مثل درخواست شبکه یا تایمر—در پس‌زمینه شروع می‌شود و جاوااسکریپت بلافاصله به خط بعدی می‌رود، بدون آنکه منتظر پایان آن عملیات بماند:

مقایسه‌ی ترتیب اجرای کد ناهمزمان
خروجی کنسول اینجا نمایش داده می‌شود...

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

چرا اصلاً به مدل Asynchronous نیاز داریم؟

از آنجا که جاوااسکریپت تک‌نخی است، اگر تمام عملیات به‌صورت Synchronous اجرا می‌شدند، هر درخواست شبکه یا هر عملیات طولانی، کل رابط کاربری را تا پایان آن عملیات کاملاً غیرقابل‌استفاده می‌کرد. مدل Asynchronous این مشکل را حل می‌کند: عملیات‌های زمان‌بر مثل fetch در پس‌زمینه (خارج از موتور اصلی جاوااسکریپت، در محیط مرورگر) انجام می‌شوند و فقط زمانی که نتیجه آماده شد، به کد اصلی بازمی‌گردند.

جدول مقایسه‌ی Synchronous و Asynchronous

ویژگی Synchronous Asynchronous
ترتیب اجرا دقیقاً به ترتیب نوشتار کد عملیات زمان‌بر به تعویق می‌افتد
اثر روی بقیه‌ی کد مسدودکننده (blocking) غیرمسدودکننده (non-blocking)
مثال رایج محاسبات ریاضی، حلقه‌های معمولی setTimeout، fetch، خواندن فایل
ابزارهای مرتبط Callback، Promise، async/await

نکات کلیدی

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

جمع‌بندی

تفاوت میان Synchronous و Asynchronous، اساس رفتار غیرمنتظره‌ای است که در بسیاری از کدهای این دوره، از جمله setTimeout و fetch، مشاهده کرده‌اید: اجرای برنامه از بالا به پایین ادامه پیدا می‌کند، بدون آنکه منتظر پایان عملیات‌های زمان‌بر بماند. در درس بعدی، مکانیزم دقیقی که این رفتار را ممکن می‌سازد—یعنی Event Loop، Call Stack و Callback Queue—به‌طور کامل بررسی خواهد شد.