افهم غير متزامن JavaScript Callbacks Promises و Async Await

تم التحديث: ٢٧ مارس ٢٠٢٦

Understand the Asynchronous Javascript Callbacks Promises and Async Await

ملخص

تطور عدم التزامن في JavaScript من الـ callbacks (التي أدت إلى تداخل "هرم الهلاك" pyramid-of-doom) إلى الـ Promises (القابلة للتسلسل، مع معالجة أفضل للأخطاء) ثم إلى async/await (كود يبدو متزامناً مع كامل قوة الـ async) — كل تجريد يبني على ما قبله، ويعالج القيود السابقة.

البرمجة غير المتزامنة (Asynchronous programming) أساسية في JavaScript. سواء كنت تجلب بيانات، أو تقرأ ملفات، أو تتعامل مع تفاعلات المستخدم، فإن العمليات غير المتزامنة موجودة في كل مكان. لكن الـ async لم يكن دائماً أنيقاً — فقد عانى المطورون سابقاً مع جحيم الـ callbacks (callback hell)، وهرم الهلاك، ومعالجة الأخطاء المتشابكة. يتتبع هذا الدليل التطور من الـ callbacks إلى الـ Promises وصولاً إلى async/await، موضحاً كيف حل كل ابتكار المشكلات السابقة مع تقديم أنماط جديدة. بنهاية الدليل، ستفهم ليس فقط كيفية استخدام async/await، بل لماذا أصبح الخيار الافتراضي في عام 2026.

الـ Callbacks: النمط الأصلي

الـ Callbacks هي دوال يتم تمريرها كوسطاء (arguments) لتنفيذها بعد اكتمال العملية.

نمط الـ Callback الأساسي

// Callback function (synchronous use)
const processData = (data, callback) => {
  callback(data.toUpperCase());
};

processData('hello', result => {
  console.log(result); // 'HELLO'
});

// Asynchronous callback
const fetchData = (url, callback) => {
  setTimeout(() => {
    callback({ id: 1, name: 'Alice' });
  }, 100);
};

fetchData('/API/user', user => {
  console.log('User:', user.name);
});

جحيم الـ Callback (هرم الهلاك)

تصبح الـ callbacks مشكلة عندما تتداخل العمليات — كل مستوى يزيد من الإزاحة، مما يخلق كوداً يصعب قراءته.

// Callback hell: deeply nested callbacks
getUser(userId, user => {
  getPosts(user.id, posts => {
    getComments(posts[0].id, comments => {
      getAuthor(comments[0].authorId, author => {
        console.log('Final author:', author.name);
        // We're 4 levels deep!
      });
    });
  });
});

// Problems:
// 1. Hard to read and maintain
// 2. Error handling is awkward (need try-catch at each level)
// 3. Code flows right, not down
// 4. Difficult to parallelize operations

معالجة الأخطاء مع الـ Callbacks

تفتقر الـ callbacks إلى معالجة مدمجة للأخطاء — يجب عليك تمرير كائنات الخطأ يدوياً أو استخدام callbacks منفصلة للأخطاء.

// Convention: error-first callbacks
const readFile = (filename, callback) => {
  setTimeout(() => {
    if (filename === 'missing.txt') {
      callback(new Error('File not found'), null);
    } else {
      callback(null, 'File contents');
    }
  }, 100);
};

readFile('data.txt', (error, data) => {
  if (error) {
    console.error('Error:', error.message);
  } else {
    console.log('Data:', data);
  }
});

// Nested error handling (tedious)
readFile('file1.txt', (err1, data1) => {
  if (err1) {
    console.error(err1);
    return;
  }
  readFile('file2.txt', (err2, data2) => {
    if (err2) {
      console.error(err2);
      return;
    }
    console.log('Both files:', data1, data2);
  });
});

الـ Promises: الـ Async القابل للتسلسل

تمثل الـ Promises قيمة مستقبلية وتوفر .then()، و .catch()، و .finally() لتركيب الكود بشكل أنظف.

إنشاء واستخدام الـ Promises

// Creating a Promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
    // or: reject(new Error('Failed!'));
  }, 100);
});

// Consuming a Promise
promise
  .then(result => {
    console.log(result); // 'Success!'
    return result + ' More work';
  })
  .then(result => {
    console.log(result); // 'Success! More work'
  })
  .catch(error => {
    console.error('Error:', error.message);
  })
  .finally(() => {
    console.log('Operation complete');
  });

التسلسل مقابل التداخل

تقضي الـ Promises على التداخل من خلال السماح بالتسلسل (chaining).

// Without Promises (callback hell)
getUser(userId, user => {
  getPosts(user.id, posts => {
    getComments(posts[0].id, comments => {
      console.log('Comments:', comments);
    });
  });
});

// With Promises (clean chain)
getUser(userId)
  .then(user => getPosts(user.id))
  .then(posts => getComments(posts[0].id))
  .then(comments => {
    console.log('Comments:', comments);
  })
  .catch(error => {
    console.error('Error:', error.message);
  });

// Promise-returning helpers
const getUser = (id) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id, name: 'Alice' });
    }, 100);
  });
};

const getPosts = (userId) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([{ id: 1, title: 'Post 1' }]);
    }, 100);
  });
};

انتشار الأخطاء

في سلاسل الـ Promise، تنتشر الأخطاء إلى أول معالج .catch().

Promise.resolve()
  .then(() => {
    throw new Error('Step 1 failed');
  })
  .then(() => {
    console.log('Step 2 (skipped)');
  })
  .then(() => {
    console.log('Step 3 (skipped)');
  })
  .catch(error => {
    console.error('Caught error:', error.message); // 'Step 1 failed'
  });

// Error recovery
Promise.reject('Network error')
  .catch(error => {
    console.log('Retrying...');
    return 'recovered'; // New Promise with value 'recovered'
  })
  .then(result => {
    console.log('Recovered:', result); // 'Recovered: recovered'
  });

العمليات المتوازية باستخدام Promise.all()

// Fetch multiple resources in parallel
const userId = 1;

Promise.all([
  fetch(`/API/users/${userId}`).then(r => r.json()),
  fetch(`/API/posts/${userId}`).then(r => r.json()),
  fetch(`/API/comments/${userId}`).then(r => r.json())
])
  .then(([user, posts, comments]) => {
    console.log('All data:', { user, posts, comments );
  })
  .catch(error => {
    console.error('One of the requests failed:', error);
  });

// Promise.race: return first completed Promise
Promise.race([
  fetch('/API/data'),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 5000)
  )
])
  .then(response => console.log('Response received'))
  .catch(error => console.error('Request timeout or failed'));

Async/Await: كود Async يبدو متزامناً

الـ async/await هو "سكر برمجي" (syntactic sugar) فوق الـ Promises يتيح لك كتابة كود async يُقرأ مثل الكود المتزامن.

الـ async/await الأساسي

// Function declaration
async function fetchUser(id) {
  // Inside async function, you can use 'await'
  const response = await fetch(`/API/users/${id}`);
  const user = await response.json();
  return user; // Returns a Promise
}

// Arrow function
const fetchPost = async (id) => {
  const response = await fetch(`/API/posts/${id}`);
  return response.json();
};

// Usage (inside an async function)
async function main() {
  const user = await fetchUser(1);
  console.log('User:', user);
}
main();

// Or, in an ES module (ES2022+), you can use top-level await directly:
// const user = await fetchUser(1);
//
// Note: outside ES modules, await can ONLY be used inside async functions.

معالجة الأخطاء باستخدام try/catch

async function fetchUserData(id) {
  try {
    const response = await fetch(`/API/users/${id}`);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    const user = await response.json();
    return user;
  } catch (error) {
    console.error('Failed to fetch user:', error.message);
    throw error; // Re-throw or return fallback
  } finally {
    console.log('Fetch attempt completed');
  }
}

// Usage (inside an async function or ES2022+ module top level)
async function run() {
  try {
    const user = await fetchUserData(1);
    console.log('User:', user);
  } catch (error{
    console.error('Error in app:', error;
  }
}
run(;

العمليات المتوازية باستخدام async/await

// Sequential: operations run one after another
async function fetchSequential(userId) {
  const user = await fetch(`/API/users/${userId`.then(r => r.json(;
  const posts = await fetch(`/API/posts/${userId`.then(r => r.json(;
  const comments = await fetch(`/API/comments/${userId`.then(r => r.json(;
  // Total time: sum of all requests
  return { user, posts, comments ;
}

// Parallel: operations start simultaneously
async function fetchParallel(userId{
  // Start all requests at once
  const userPromise = fetch

نشرة أسبوعية مجانية

ابقَ على مسار النيرد

بريد واحد أسبوعياً — دورات، مقالات معمّقة، أدوات، وتجارب ذكاء اصطناعي.

بدون إزعاج. إلغاء الاشتراك في أي وقت.