Asynchronous vs Synchronous Programming

✅ أولاً: ما معنى Synchronous و Asynchronous؟
🔹 Synchronous Programming (البرمجة المتزامنة)
  • تعني أن كل سطر من الكود يتم تنفيذه بعد الانتهاء من السطر السابق.
  • JavaScript افتراضيًا لغة Single-threaded (تعمل بسلسلة تنفيذ واحدة)، مما يعني أنها تنفذ الكود سطرًا تلو الآخر.

في JavaScript، عندما نقول إن اللغة Single-threaded “أحادية الخيط”، فإن ذلك يعني:

JavaScript تنفذ التعليمات واحدة تلو الأخرى في تسلسل، على مسار واحد فقط (خيط واحد – Thread).

ما المقصود بـ “Thread”؟

الخيط (Thread) هو المسار الذي تسير فيه التعليمات داخل المعالج. بعض اللغات مثل Java أو C++ يمكنها تشغيل أكثر من خيط في نفس الوقت (Multithreading)، ولكن JavaScript لا تفعل ذلك بشكل مباشر.

لماذا JavaScript مصممة بهذه الطريقة؟

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

ماذا يعني هذا للمطورين؟
  • لا يمكن تنفيذ أكثر من مهمة في نفس اللحظة داخل JavaScript.
  • إذا كانت هناك عملية بطيئة (مثل جلب بيانات من الإنترنت أو قراءة ملف كبير)، فإنها قد تعطل باقي التعليمات إذا لم تتم إدارتها بطريقة غير متزامنة (asynchronous).
كيف تتعامل JavaScript مع المهام الطويلة؟

رغم أنها Single-threaded، إلا أن JavaScript تدعم آليات غير متزامنة (Asynchronous) مثل:

  • setTimeout
  • Promises
  • async/await
  • Web APIs (مثل fetch)
  • Event Loop

هذه الأدوات تمنح وهم التعدد (pseudo-concurrency) من خلال تفويض المهام الطويلة إلى Web APIs خارج الخيط الأساسي، ثم تعيد نتائجها لاحقًا دون حجز الخيط.

ملخص:

Single-threaded تعني أن JavaScript تنفذ كودًا واحدًا في وقت واحد، لكن يمكنها التعامل مع المهام الطويلة بطريقة ذكية باستخدام آليات غير متزامنة.

🧠 مثال:
console.log("1");
console.log("2");
alert("test");
console.log("3");

////////////////////////////////
1
2
3
JavaScript

يتم تنفيذ الأوامر بالترتيب، وكل أمر ينتظر السابق حتى ينتهي.

🔹 Asynchronous Programming (البرمجة غير المتزامنة)
  • تسمح بتنفيذ بعض الأوامر بشكل غير متزامن، أي لا تنتظر تنفيذ الأوامر الأخرى.
  • تُستخدم لتحسين الأداء عند التعامل مع عمليات تستغرق وقتًا (مثل: طلب بيانات من API، أو قراءة ملفات).
✅ أمثلة عملية
📌 مثال على Asynchronous باستخدام setTimeout
console.log("1");

setTimeout(() => {
  console.log("2");
}, 2000); // ينتظر ثانيتين

console.log("3")
/////////////////////////
1
3
2
JavaScript

🧠 الشرح:

  • يتم تنفيذ 1.
  • ثم يتم جدولة 2 ليتم تنفيذه بعد ثانيتين.
  • يتم تنفيذ 3 مباشرة دون انتظار 2.
  • بعد ثانيتين، يُنفذ الكود داخل setTimeout.
✅ كيف تدير JavaScript العمليات غير المتزامنة؟

JavaScript تستخدم ما يُعرف بـ Event Loop:

  1. تنفذ الـ Call Stack الأوامر المتزامنة.
  2. الأوامر غير المتزامنة تُرسل إلى Web APIs (مثل setTimeout, fetch).
  3. عند الانتهاء، تُرسل إلى Callback Queue.
  4. عندما تصبح الـ Call Stack فارغة، ينقل الـ Event Loop الأوامر من الـ Callback Queue لتنفيذها.
✅ Promises (الوعود) – طريقة حديثة للتعامل مع Asynchronous
📌 مثال باستخدام Promise:
let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("تمت العملية!");
  }, 2000);
});

promise.then(result => {
  console.log(result); // "تمت العملية!" بعد ثانيتين
});
JavaScript

✅ Async / Await – أسلوب حديث مبني على Promises لكتابة كود يبدو متزامنًا لكنه غير متزامن

function getData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("البيانات جاهزة");
    }, 2000);
  });
}

async function showData() {
  console.log("1");
  let result = await getData(); // ينتظر حتى يتم حل الـ Promise
  console.log(result); // "البيانات جاهزة"
  console.log("3");
}

showData();
JavaScript
1
(انتظار ثانيتين)
البيانات جاهزة
3
JavaScript
✅ الفرق الجوهري
الخاصيةSynchronousAsynchronous
الترتيبتنفيذي صارميمكن تجاوز بعض الأوامر
الأداءبطيء مع العمليات الثقيلةسريع ومناسب للعمليات المؤجلة
الاستخدامالأوامر البسيطةAPI, ملفات, عمليات معقدة
التعاملمباشريستخدم Callbacks / Promises / Async-Await
✅ خلاصة:
  • البرمجة المتزامنة: تنفذ الأوامر واحدًا تلو الآخر.
  • البرمجة غير المتزامنة: تسمح بتنفيذ أوامر دون انتظار أوامر أخرى.
  • JavaScript تستخدم أساليب مثل: setTimeout, fetch, Promise, و async/await للتعامل مع البرمجة غير المتزامنة.

ما هو الـ Call Stack؟

✅ تعريف:

الـ Call Stack (مكدس الاستدعاء) هو المكان الذي تُسجّل فيه JavaScript جميع الدوال التي يجب تنفيذها.

هو عبارة عن مكدّس (Stack) يعمل بطريقة LIFO (آخر من يدخل هو أول من يخرج).

🧠 كيف يعمل؟

عند استدعاء دالة:

  1. تُضاف إلى الـ Call Stack.
  2. تُنفذ الدالة.
  3. بعد انتهاء التنفيذ، تُزال من الـ Stack.
📌 مثال:
function greet() {
  console.log("Hello!");
}

function start() {
  greet();
}

start();
JavaScript

🔁 خطوات التنفيذ في الـ Call Stack:

  1. start() تدخل إلى الـ Stack
  2. داخل start(), يتم استدعاء greet()
  3. greet() تدخل إلى الـ Stack
  4. console.log("Hello!") ينفذ
  5. greet() تنتهي وتخرج من الـ Stack
  6. start() تنتهي وتخرج من الـ Stack
function first() {
  console.log("1: First function");
  second();
  console.log("1: Back to First function");
}

function second() {
  console.log("2: Second function");
  third();
  console.log("2: Back to Second function");
}

function third() {
  console.log("3: Third function");
}

first();
JavaScript
✅ ما الذي يحدث داخل الـ Call Stack خطوة بخطوة؟
  1. يتم استدعاء first() → تدخل إلى الـ Stack.
  2. تطبع: 1: First function
  3. تستدعي second() → تدخل فوق first() في الـ Stack.
  4. تطبع: 2: Second function
  5. تستدعي third() → تدخل فوق second() في الـ Stack.
  6. تطبع: 3: Third function
  7. تنتهي third()تخرج أولًا من الـ Stack (لأنها آخر من دخل).
  8. نعود إلى second() وتُكمل: تطبع 2: Back to Second function → ثم تخرج.
  9. نعود إلى first() وتُكمل: تطبع 1: Back to First function → ثم تخرج.
🔷ما هي الـ Web API؟
✅ تعريف:

الـ Web API هي مجموعة من الوظائف التي توفرها المتصفح (وليس JavaScript نفسها)، وتُستخدم للقيام بمهام مثل:

  • setTimeout
  • fetch
  • التعامل مع DOM
  • الأحداث addEventListener

JavaScript نفسها لا تملك setTimeout – بل هي وظيفة مقدمة من Web APIs في المتصفح.

🧠 كيف تعمل Web API مع Call Stack؟

دعنا نأخذ مثالًا توضيحيًا 👇

📌 مثال:
console.log("1");

setTimeout(() => {
  console.log("2");
}, 2000);

console.log("3");
JavaScript
✅ ماذا يحدث وراء الكواليس؟
  1. console.log("1") → تدخل إلى Call Stack → تنفذ → تخرج
  2. setTimeout(...) → تدخل إلى Call Stack → يُرسل الـ callback إلى Web API → تخرج
  3. console.log("3") → تدخل إلى Call Stack → تنفذ → تخرج
  4. بعد 2 ثانية، تنتهي Web API من المؤقت → ترسل الـ callback (() => console.log("2")) إلى Callback Queue
  5. الـ Event Loop يلاحظ أن الـ Call Stack فارغ → ينقل الدالة من Callback Queue إلى Call Stack
  6. console.log("2") → تنفذ وتخرج

🔄 التخطيط الزمني:

Call Stack:     |  console.log("1")  |  setTimeout(...)  |  console.log("3")  |  () => console.log("2")
Web API:        |      --            |     timer runs    |        --           |         --
Callback Queue: |      --            |        --         |        --           |  () => console.log("2")
Event Loop:     | ينقل callback من الـ queue عندما يكون Call Stack فارغًا
JavaScript

🖼️ رسم توضيحي مبسط:

[ Call Stack ]
|             |
| console.log(2)  ← Event Loop ينقلها من Callback Queue
|_____________|

[ Callback Queue ]
| () => console.log("2") |

[ Web APIs ]
| setTimeout running...  |

[ Event Loop ]
     👁️ يراقب Call Stack وينقل الدوال الجاهزة من Callback Queue
JavaScript
✅ خلاصة:
المكونالوظيفة
Call Stackينفذ الكود سطرًا بسطر (المتزامن)
Web APIsتدير العمليات غير المتزامنة مثل setTimeout و fetch
Callback Queueتنتظر فيها الدوال غير المتزامنة لحين تنفيذها
Event Loopينقل الدوال الجاهزة من Queue إلى Stack
🔷 ما هو Event Loop؟
✅ تعريف:

الـ Event Loop هو الآلية التي تسمح لـ JavaScript (التي تعمل بشكل متزامن Single-threaded) بأن تدير عمليات غير متزامنة بكفاءة، مثل:

  • setTimeout
  • fetch
  • أحداث click
  • Promises
🔷 ما هي Callback Queue؟
✅ تعريف:

الـ Callback Queue (طابور الاستدعاءات الراجعة) هي المكان الذي تنتظر فيه الدوال (Callbacks) التي تأتي من Web APIs بعد انتهاء تنفيذها، حتى يسمح لها الـ Event Loop بالدخول إلى Call Stack وتنفيذها.

🔁 كيف يعمل النظام كاملًا؟ (بترتيب الخطوات)
تخيل الكود التالي:
console.log("1");

setTimeout(() => {
  console.log("2");
}, 2000);

console.log("3");
JavaScript
🔄 خطوات التنفيذ:
  1. console.log("1") → يُنفذ مباشرة داخل الـ Call Stack.
  2. setTimeout(...) → يُمرر إلى Web API الخاصة بالمتصفح، ويبدأ العداد (2 ثانية).
  3. console.log("3") → يُنفذ مباشرة بعد setTimeout.
  4. بعد مرور ثانيتين:
    • يُنقل الـ () => console.log("2") إلى Callback Queue.
  5. الـ Event Loop يراقب الـ Call Stack، وبمجرد أن يكون فارغًا:
    • ينقل الدالة من Callback Queue إلى Call Stack لتنفيذها.

🖼️ تصور بصري للحركة

1. Call Stack:     console.log("1") → يُنفذ
2. Web API:        setTimeoutيبدأ العداد
3. Call Stack:     console.log("3") → يُنفذ
4. بعد 2 ثانية:
   - Web API تنقل () => console.log("2") إلى Callback Queue
5. Event Loop:     يلاحظ أن Call Stack فارغينقل () => console.log("2") لتنفيذه
JavaScript
✅ ملخص المكونات الرئيسية:
المكونالوظيفة
Call Stackينفذ الأوامر واحدًا تلو الآخر
Web APIsتدير العمليات غير المتزامنة (مثل timers و fetch و DOM events)
Callback Queueتخزن الدوال الراجعة callbacks المنتظرة ليتم تنفيذها لاحقًا
Event Loopينقل الـ callbacks من الـ Callback Queue إلى Call Stack عند الفراغ

📌 مثال عملي أكثر تعقيدًا:

console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise");
});

console.log("End");
JavaScript
Start
End
Promise
Timeout
JavaScript
❓ لماذا؟
  • console.log("Start") → يُنفذ
  • setTimeout(..., 0) → يُرسل إلى Web API، وتنتقل الدالة إلى Callback Queue
  • Promise.then(...) → يُرسل إلى Microtask Queue
  • console.log("End") → يُنفذ
  • بعد الانتهاء من Call Stack، يُفضل الـ Event Loop Microtask Queue (مثل Promise) قبل Callback Queue.
  • ثم ينفذ الكود داخل then()
  • وأخيرًا، ينفذ الدالة داخل setTimeout
✅ Microtask Queue vs Callback Queue
النوعالاستخدامأولوية التنفيذ
Callback QueuesetTimeout, DOM eventsأقل أولوية
Microtask QueuePromises, async/awaitأعلى أولوية، تُنفذ قبل الـ Callbacks
🎯 خلاصة:
  • الـ Event Loop هو القلب النابض الذي ينسق بين Call Stack و Web APIs و Queues.
  • عندما ينتهي تنفيذ المهام المتزامنة، يبدأ Event Loop في إدخال المهام غير المتزامنة (callbacks) من الطوابير.
  • Promises (Microtasks) دائمًا تُنفذ قبل setTimeout (Callbacks) إذا كانت جاهزة.