JavaScript Async/Await

مقدمة: ليه async مهم؟

تخيل انك في مطعم وطلبت شاي. المطعم مش هيقف في الطابور ويستنى الشاي ينخلص ليهم علشان يقبلوا طلبك التاني – بالعكس، هيتقبلوا طلبياتك التانية وفي نفس الوقت الشاي بيتحضر في المطبخ.

JavaScript بنفس الفكرة – async هي الطريقة اللي JavaScript تشتغل بيها عمليات طويلة بدون ما توقف البرنامج كله.

الجزء الأول: Callbacks (الطريقة القديمة)

في البداية، كانت JavaScript تستخدم Callbacks – يعني دالة تاخد دالة تانية كمعامل:

function fetchData(callback) {
  setTimeout(() => {
    callback("البيانات وصلت!");
  }, 2000);
}

fetchData((data) => {
  console.log(data); // البيانات وصلت!
});

المشكلة: لما تتسلسل عمليات كتير، الكود بيصير معقد جداً (“Callback Hell”):

fetchData(() => {
  processData(() => {
    saveData(() => {
      console.log("خلص!");
    });
  });
});

بتشوف؟ كل عملية جواة عملية – بتصير متاهة.

الجزء الثاني: Promises (الخطوة الوسيطة)

في 2015 جت Promises – وهي طريقة أنظف شوية:

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("البيانات وصلت!");
    }, 2000);
  });
}

fetchData()
  .then((data) => {
    console.log(data);
    return processData(data);
  })
  .then((result) => {
    console.log(result);
    return saveData(result);
  })
  .catch((error) => {
    console.log("في خطأ:", error);
  });

أحسن من Callbacks، لكن لسة الكود مش سهل القراية مقارنة بالـ synchronous code.

الجزء الثالث: Async/Await (الطريقة الحديثة) ✨

في 2017، جت async/await – وهي بتخليك تكتب الكود كأنه synchronous، لكنه بيشتغل asynchronously:

async function main() {
  try {
    const data = await fetchData();
    console.log(data);
    
    const result = await processData(data);
    console.log(result);
    
    await saveData(result);
    console.log("خلص!");
  } catch (error) {
    console.log("في خطأ:", error);
  }
}

main();

بتقول ايه؟ الكود بسيط وواضح ومسهل القراية!

إزاي async/await بتشتغل؟

كلمة async:

كل دالة async بترجع Promise دايماً:

async function greeting() {
  return "مرحباً!";
}

greeting().then(msg => console.log(msg)); // "مرحباً!"

كلمة await:

بتوقف تنفيذ الدالة لحد ما الـ Promise تتحل:

async function main() {
  console.log("1"); // يطبع فوراً
  
  await new Promise(resolve => setTimeout(resolve, 2000));
  
  console.log("2"); // يطبع بعد 2 ثانية
}

main();

أمثلة عملية حقيقية

مثال 1: جلب البيانات من API

async function getUserData(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.log("خطأ في جلب البيانات:", error);
  }
}

getUserData(1);

مثال 2: عمليات متعددة بالتوازي

async function getMultipleUsers(ids) {
  // الطريقة البطيئة (متسلسلة):
  // const user1 = await fetchUser(ids[0]);
  // const user2 = await fetchUser(ids[1]);
  
  // الطريقة السريعة (متوازية):
  const users = await Promise.all(
    ids.map(id => fetchUser(id))
  );
  
  return users;
}

مثال 3: إعادة محاولة (Retry Logic)

async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      return await response.json();
    } catch (error) {
      if (i === retries - 1) throw error;
      console.log(`محاولة ${i + 1} فشلت، محاولة مرة تانية...`);
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
}

الأخطاء الشائعة

خطأ 1: نسيان await

// ❌ غلط
async function main() {
  const data = fetchData(); // هترجع Promise، مش البيانات
  console.log(data); // Promise { <pending> }
}

// ✅ صح
async function main() {
  const data = await fetchData(); // هتستنى البيانات
  console.log(data); // البيانات فعلاً
}

خطأ 2: عدم التعامل مع الأخطاء

// ❌ خطر - لو حصل error، البرنامج قد يتوقف
async function main() {
  const data = await fetchData();
}

// ✅ آمن - بنتعامل مع الأخطاء
async function main() {
  try {
    const data = await fetchData();
  } catch (error) {
    console.log("حصلت مشكلة:", error);
  }
}

خطأ 3: operations متسلسلة بدل متوازية

// ❌ بطيء - كل عملية تستنى التانية
async function slow() {
  const data1 = await fetchData(1); // 2 ثانية
  const data2 = await fetchData(2); // 2 ثانية
  // المجموع = 4 ثواني
}

// ✅ سريع - العمليات متوازية
async function fast() {
  const [data1, data2] = await Promise.all([
    fetchData(1),
    fetchData(2)
  ]);
  // المجموع = 2 ثانية
}

نصائح ذهبية

  1. استخدم try/catch دايماً – علشان تتعامل مع الأخطاء بشكل آمن
  2. استخدم Promise.all لـ parallel operations – لو العمليات مش معتمدة على بعضها
  3. لا تستخدم async في الحلقات بدون تفكير – قد تبطيء الأداء
  4. async functions ترجع Promise دايماً – حتى لو ما كتبت return
  5. استخدم await في async functions فقط – ما تقدرش تستخدم await في دالة عادية

الخلاصة

  • Callbacks قديمة وتسبب كود معقد
  • Promises أفضل لكن لسة معقدة
  • Async/Await الطريقة الحديثة والأنظف

استخدم async/await في كل الحالات الحديثة – الكود بيصير أسهل وأوضح وأأمن.


سؤال ليك: إيه آخر عملية async عملتها في كودك وواجهت فيها مشكلة؟ 😉

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *