البرمجة فيها نوعين أساسيين: Synchronous وAsynchronous، ولما نفهم الفرق بينهم، هيبقى أسهل نعرف ليه الـ Asynchronous مهم جدًا.
📌 الـ Synchronous Programming:
ده يعني إن الكود بيتنفذ خطوة بخطوة، بمعنى إن الكود مبيكملش تنفيذ أي خطوة تانية إلا لما الخطوة اللي قبلها تخلص. يعني لو عندك عملية بتاخد وقت زي إنك بتحمل بيانات من السرفر عن طريق الـ API، الكود هيفضل مستني لحد ما البيانات تتحمل قبل ما يكمل باقي التنفيذ.
📌 الـ Asynchronous Programming:
أما في البرمجة Asynchronous، الكود يقدر ينفذ حاجات كتير في نفس الوقت من غير ما يستنى العملية الطويلة تخلص. يعني لو عندك عملية زي تحميل البيانات من السرفر، الكود ممكن يستمر في تنفيذ أكواد تانية، ولما البيانات تتحمل، الكود هيعرف ويتعامل مع النتيجة.
📌 إزاي الكلام ده بيحصل في JavaScript؟
في JavaScript، عندنا طريقتين رئيسيتين للتعامل مع الـ asynchronous programming
🔻 الـ Callbacks 🔻 الـ Promises
(وطبعًا async/await اللي هو تحسين للـ Promises)
📌 الـ Callbacks
أول طريقة للتعامل مع الـ asynchronous programming كانت عن طريق الـ Callbacks. الفكرة ببساطة إنك بتمرر دالة معينة كـ "callback" للكود اللي بيتنفذ، ولما الكود ده يخلص، الدالة دي بتشتغل.
لكن الـ callbacks بتتحول بسرعة لكود معقد جدًا لما يكون عندك عمليات كتير وبتحتاج تكتب حاجات كتير جوة بعضها، وده اللي بنسميه Callback Hell.
📌 الحل: الـ Promises
هنا بيجي دور الـ Promises، اللي هي طريقة جديدة وأكثر تنظيمًا للتعامل مع العمليات الـ asynchronous. الـ Promise هو في الأساس "وعد" إن العملية هتخلص في المستقبل.
الـ Promise بيكون ليه 3 حالات:
🔻 حالة الـ Pending: يعني العملية لسه مخلصتش. 🔻 حالة الـ Fulfilled: يعني العملية نجحت وطلعت النتيجة. 🔻 حالة الـ Rejected: يعني العملية فشلت وفيه خطأ حصل.
لما العملية تخلص بنجاح، الـ Promise بيتحول لحالة "fulfilled"، ولو العملية فشلت بيتحول لحالة "rejected".
خلينا نشوف مثال عشان نفهم الكلام ده بشكل أوضح. هنفترض إننا عايزين نحاكي عملية استرجاع بيانات من الـ API.
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const dataAvailable = true;
if (dataAvailable) {
resolve("Data retrieved successfully!");
} else {
reject("Error: Unable to retrieve data.");
}
}, 3000);
});
};
fetchData()
.then((result) => console.log(result))
.catch((error) => console.log(error));
في الكود ده، الدالة fetchData بترجع Promise. داخل الـ Promise، بنستخدم إما resolve لو العملية نجحت أو reject لو حصل خطأ.
باستخدام then، بنقدر نتعامل مع النتيجة لو العملية نجحت، ولو حصل خطأ، الـ catch بتساعدنا في التعامل معاه.
📌 الـ Async/Await
علشان نخلي الكود أبسط وأوضح، ظهر حاجة اسمها async/await اللي بتشتغل على الـ Promises بس بشكل مبسط جدًا وبتخلي الكود يبان كأنه synchronous.
نفس المثال اللي اللي فات نقدر نكتبه بطريقة async/await بالشكل ده:
const fetchData = async () => {
try {
const result = await new Promise((resolve, reject) => {
setTimeout(() => {
const dataAvailable = true;
if (dataAvailable) {
resolve("Data retrieved successfully!");
} else {
reject("Error: Unable to retrieve data.");
}
}, 3000);
});
console.log(result);
} catch (error) {
console.log(error);
}
};
fetchData();
الميزة هنا إننا استخدمنا await عشان نستنى النتيجة من الـ Promise وكأن العملية دي synchronous، لكن في الحقيقة الكود شغال بطريقة asynchronous.
وبدل ما نستخدم then و catch، استخدمنا try و catch علشان نتعامل مع النجاح أو الفشل.
📌 ليه أستخدم الـ Promises والـ Async/Await؟
✅ تنظيم الكود: الكود بيبقى أنضف وأبسط في الفهم مقارنة بالـ Callbacks. ✅ الـ Error Handling أفضل: التعامل مع الأخطاء بقى أسهل باستخدام try/catch. ✅ لما يكون عندك مشروع كبير، الـ async/await هيساعدك في إدارة الأكواد الطويلة والمعقدة بسهولة.
بالتوفيق يا بطل 💪🏻