السبت - الخميس: 10:00 ص - 10:00 م

البرمجة غير المتزامنة في JavaScript: Promises و Async/Await

البرمجة غير المتزامنة في JavaScript: Promises و Async/Await

البرمجة غير المتزامنة في JavaScript

البرمجة غير المتزامنة (Asynchronous Programming) هي أحد أهم المفاهيم في JavaScript. تسمح للموقع بالاستجابة للمستخدمين أثناء تنفيذ العمليات التي تستغرق وقتاً، مثل جلب البيانات من الخادم أو معالجة الملفات.

لماذا نحتاج البرمجة غير المتزامنة؟

بدون البرمجة غير المتزامنة، إذا كان موقعك يجلب بيانات من خادم، فسيتم تجميد الموقع بالكامل حتى تنتهي العملية. هذا يعني أن المستخدم لن يتمكن من النقر على أي شيء أو التفاعل مع الموقع.

تطور البرمجة غير المتزامنة في JavaScript:

1️⃣ Callbacks (الاستدعاءات الراجعة)

الطريقة الأولى للتعامل مع العمليات غير المتزامنة

// مثال على Callback
function fetchData(callback) {
  setTimeout(() => {
    callback('البيانات جاهزة!');
  }, 1000);
}

fetchData((data) => {
  console.log(data); // ستظهر بعد ثانية واحدة
});

2️⃣ Promises (الوعود)

تحسين على Callbacks مع معالجة أفضل للأخطاء

// مثال على Promise
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('البيانات جاهزة!');
    }, 1000);
  });
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.error(error));

3️⃣ Async/Await (الانتظار غير المتزامن)

الطريقة الأحدث والأسهل للتعامل مع العمليات غير المتزامنة

// مثال على Async/Await
async function fetchData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

Callbacks - البداية:

مثال عملي على Callbacks:

// جلب بيانات المستخدم
function getUserData(userId, callback) {
  // محاكاة طلب للخادم
  setTimeout(() => {
    const user = {
      id: userId,
      name: 'أحمد محمد',
      email: '[email protected]'
    };
    callback(null, user); // null للأخطاء، user للبيانات
  }, 1000);
}

// استخدام Callback
getUserData(123, (error, user) => {
  if (error) {
    console.error('خطأ في جلب البيانات:', error);
  } else {
    console.log('بيانات المستخدم:', user);
  }
});

مشاكل Callbacks:

  • Callback Hell - تعشيق Callbacks داخل بعضها
  • صعوبة معالجة الأخطاء
  • صعوبة القراءة والصيانة

Promises - الحل الأفضل:

إنشاء Promise:

const myPromise = new Promise((resolve, reject) => {
  // العملية غير المتزامنة
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('تم بنجاح!');
    } else {
      reject('حدث خطأ!');
    }
  }, 1000);
});

// استخدام Promise
myPromise
  .then(result => console.log(result))
  .catch(error => console.error(error));

مثال عملي - جلب البيانات:

function fetchUserPosts(userId) {
  return new Promise((resolve, reject) => {
    // محاكاة طلب API
    setTimeout(() => {
      const posts = [
        { id: 1, title: 'مقال 1', content: 'محتوى المقال الأول' },
        { id: 2, title: 'مقال 2', content: 'محتوى المقال الثاني' }
      ];
      resolve(posts);
    }, 1500);
  });
}

// استخدام Promise
fetchUserPosts(123)
  .then(posts => {
    console.log('المقالات:', posts);
    posts.forEach(post => {
      console.log(post.title);
    });
  })
  .catch(error => {
    console.error('خطأ في جلب المقالات:', error);
  });

Promise Methods:

  • Promise.all() - انتظار عدة Promises
  • Promise.race() - أول Promise ينتهي
  • Promise.allSettled() - جميع Promises بغض النظر عن النتيجة

Async/Await - الأسهل والأوضح:

التحويل من Promise إلى Async/Await:

// باستخدام Promise
function getData() {
  return fetch('/api/data')
    .then(response => response.json())
    .then(data => {
      console.log(data);
      return data;
    })
    .catch(error => {
      console.error('خطأ:', error);
    });
}

// باستخدام Async/Await
async function getData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error('خطأ:', error);
  }
}

مثال متقدم - معالجة متعددة:

async function getUserProfile(userId) {
  try {
    // جلب بيانات المستخدم
    const userResponse = await fetch(`/api/users/${userId}`);
    const user = await userResponse.json();
    
    // جلب مقالات المستخدم
    const postsResponse = await fetch(`/api/users/${userId}/posts`);
    const posts = await postsResponse.json();
    
    // جلب أصدقاء المستخدم
    const friendsResponse = await fetch(`/api/users/${userId}/friends`);
    const friends = await friendsResponse.json();
    
    return {
      user,
      posts,
      friends
    };
  } catch (error) {
    console.error('خطأ في جلب الملف الشخصي:', error);
    throw error;
  }
}

// استخدام الدالة
getUserProfile(123)
  .then(profile => {
    console.log('الملف الشخصي:', profile);
  })
  .catch(error => {
    console.error('فشل في جلب الملف الشخصي:', error);
  });

مقارنة بين الطرق المختلفة:

Callbacks

  • ✅ بسيط للمهام البسيطة
  • ❌ Callback Hell
  • ❌ صعوبة معالجة الأخطاء
  • ❌ صعوبة القراءة

Promises

  • ✅ معالجة أفضل للأخطاء
  • ✅ تجنب Callback Hell
  • ✅ سهولة القراءة
  • ❌ قد يكون معقداً أحياناً

Async/Await

  • ✅ الأسهل في القراءة
  • ✅ معالجة أخطاء بسيطة
  • ✅ يشبه الكود المتزامن
  • ✅ أفضل للمبتدئين

أمثلة عملية متقدمة:

1. معالجة متعددة مع Promise.all:

async function loadDashboardData() {
  try {
    const [users, posts, comments] = await Promise.all([
      fetch('/api/users').then(res => res.json()),
      fetch('/api/posts').then(res => res.json()),
      fetch('/api/comments').then(res => res.json())
    ]);
    
    return { users, posts, comments };
  } catch (error) {
    console.error('خطأ في تحميل بيانات لوحة التحكم:', error);
  }
}

2. معالجة الأخطاء المتقدمة:

async function robustDataFetch(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      console.log(`محاولة ${i + 1} فشلت:`, error.message);
      if (i === retries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

3. تحميل متوازي مع معالجة الأخطاء:

async function loadUserData(userId) {
  const promises = [
    fetch(`/api/users/${userId}`).then(res => res.json()),
    fetch(`/api/users/${userId}/posts`).then(res => res.json()),
    fetch(`/api/users/${userId}/friends`).then(res => res.json())
  ];
  
  const results = await Promise.allSettled(promises);
  
  return {
    user: results[0].status === 'fulfilled' ? results[0].value : null,
    posts: results[1].status === 'fulfilled' ? results[1].value : [],
    friends: results[2].status === 'fulfilled' ? results[2].value : []
  };
}

أفضل الممارسات:

  • استخدم Async/Await للكود الجديد
  • تعامل مع الأخطاء باستخدام try/catch
  • استخدم Promise.all() للعمليات المتوازية
  • تجنب Async/Await في الحلقات - استخدم Promise.all()
  • استخدم Promise.race() للعمليات التي لها مهلة زمنية
  • لا تنس await في الدوال async

أخطاء شائعة:

  • ❌ نسيان await
  • ❌ عدم معالجة الأخطاء
  • ❌ استخدام async في الحلقات
  • ❌ عدم فهم أن async/await يعيد Promise
  • ❌ خلط بين Promise و Async/Await

البرمجة غير المتزامنة هي مهارة أساسية في JavaScript. أتقنها وستصبح مطوراً أفضل!

تعلم JavaScript المتقدم معنا

تواصل معنا

عبر الماسنجر او الهاتف