المشكلة: JavaScript متزامن بطبعه
JavaScript يُنفّذ الكود سطراً بسطر. لكن بعض العمليات تأخذ وقتاً:
- جلب بيانات من API
- قراءة ملف
- استعلام قاعدة بيانات
ما الحلّ؟ نُخبر JavaScript أن ينتظر النتيجة بدون أن يوقف البرنامج.
التطوّر: من Callbacks إلى Async/Await
Callbacks (الطريقة القديمة):
getUser(1, (err, user) => {
if (err) return handleError(err);
getPosts(user.id, (err, posts) => {
if (err) return handleError(err);
getComments(posts[0].id, (err, comments) => {
// هذا يُسمّى "callback hell" 😱
});
});
});Promises (تحسين):
getUser(1)
.then((user) => getPosts(user.id))
.then((posts) => getComments(posts[0].id))
.then((comments) => console.log(comments))
.catch(handleError);async/await (الأنظف):
async function loadData() {
try {
const user = await getUser(1);
const posts = await getPosts(user.id);
const comments = await getComments(posts[0].id);
console.log(comments);
} catch (err) {
handleError(err);
}
}الأخير أسهل قراءةً ويشبه الكود المتزامن.
قواعد async/await
awaitيعمل فقط داخل دالةasync(أو على أعلى مستوى في ES modules)await"ينتظر" نتيجة الـ Promise- الدالة المُعلَّمة بـ
asyncترجع Promise دائماً
مثال كامل: جلب بيانات من API
async function fetchUser(id) {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) {
throw new Error(`خطأ ${response.status}`);
}
const user = await response.json();
return user;
}
// الاستخدام
fetchUser(1)
.then(user => console.log(user))
.catch(err => console.error(err));
// أو بداخل async أخرى
async function main() {
try {
const user = await fetchUser(1);
console.log(user);
} catch (err) {
console.error("فشل:", err.message);
}
}
main();Promise.all: التوازي لسرعة أفضل
إن لم تكن العمليات مترابطة، نفّذها بالتوازي:
// ❌ بطيء (3 طلبات متسلسلة)
async function loadAllSlow() {
const users = await fetch("/api/users").then(r => r.json());
const posts = await fetch("/api/posts").then(r => r.json());
const tags = await fetch("/api/tags").then(r => r.json());
return { users, posts, tags };
}
// ✅ سريع (3 طلبات متوازية)
async function loadAllFast() {
const [users, posts, tags] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json()),
fetch("/api/tags").then(r => r.json()),
]);
return { users, posts, tags };
}لو كل طلب يأخذ ثانية، الأولى تأخذ 3 ثوانٍ والثانية ثانية واحدة.
معالجة الأخطاء
try/catch
async function safeFetch(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error("خطأ:", err.message);
return null; // أو أعد رمي الخطأ إن أردت
}
}على مستوى Promise.all
إن فشل أيّ واحد، الكل يفشل:
try {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
} catch (err) {
// فشل A أو B أو كلاهما
}للسماح بفشل بعضها واستمرار الآخر، استخدم Promise.allSettled:
const results = await Promise.allSettled([fetchA(), fetchB()]);
results.forEach((r, i) => {
if (r.status === "fulfilled") {
console.log(`#${i}: نجح`, r.value);
} else {
console.log(`#${i}: فشل`, r.reason);
}
});مثال واقعي: نظام تسجيل دخول
async function login(email, password) {
try {
// 1. إرسال بيانات تسجيل الدخول
const res = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!res.ok) {
if (res.status === 401) throw new Error("بيانات خاطئة");
throw new Error("خطأ في الخادم");
}
const { token } = await res.json();
// 2. حفظ التوكن
localStorage.setItem("token", token);
// 3. جلب بيانات المستخدم
const userRes = await fetch("/api/me", {
headers: { Authorization: `Bearer ${token}` },
});
const user = await userRes.json();
return user;
} catch (err) {
console.error("فشل تسجيل الدخول:", err.message);
throw err;
}
}
// الاستخدام في نموذج
document.getElementById("login-form").addEventListener("submit", async (e) => {
e.preventDefault();
const form = e.target;
try {
const user = await login(form.email.value, form.password.value);
alert(`أهلاً ${user.name}`);
} catch (err) {
alert(err.message);
}
});أخطاء شائعة
نسيان await
// ❌ data هنا Promise، ليس القيمة الفعلية
async function broken() {
const data = fetch("/api/x").then(r => r.json());
console.log(data); // Promise {...}
}
// ✅
async function fixed() {
const data = await fetch("/api/x").then(r => r.json());
console.log(data); // القيمة الحقيقية
}استخدام await خارج async
// خطأ
function bad() {
const x = await fetch("/api"); // SyntaxError
}
// صحيح
async function good() {
const x = await fetch("/api");
}forEach لا يدعم await
// ❌ لا يعمل كما تتوقّع
items.forEach(async (item) => {
await process(item); // forEach لا ينتظر
});
console.log("انتهى"); // يُطبع قبل انتهاء العمليات
// ✅ استخدم for...of
for (const item of items) {
await process(item);
}
console.log("انتهى فعلاً");الأسئلة الشائعة
ما الفرق بين async/await و Promises؟
الاثنان يمثّلان نفس المفهوم — async/await هو سكّر نحوي (syntactic sugar) فوق Promises. تحت الغطاء، الكود نفسه.
هل أستخدم async مع كل دالة؟
لا — فقط مع الدوال التي تحتوي await. إضافة async لدالة عادية يحوّل قيمتها إلى Promise دون حاجة.
هل async يعني "متعدّد الخيوط"؟
لا — JavaScript ما زال single-threaded. async/await يسمح لك بإدارة العمليات اللا-متزامنة بشكل أنيق، لكن التنفيذ يبقى خيطاً واحداً.
مقالات ذات صلة
OOP في Python: البرمجة الكائنية بأمثلة عملية
البرمجة الكائنية تنظّم كودك وتسهّل صيانته. تعلّم الكلاسات، الوراثة، والتغليف في Python.
TypeScript المتقدّم: Generics والأنواع المتقدّمة بأمثلة
ترقَ من TypeScript المبتدئ إلى المتقدّم — Generics، Utility Types، Conditional Types، و template literals.
Python للمبتدئين بالعربي: درس شامل من الصفر
ابدأ تعلّم Python من الصفر بالعربية: المتغيّرات، الشروط، الحلقات، الدوال، والقوائم. أمثلة عملية وأخطاء شائعة.