مقدمة في البرمجة غير المتزامنة باستخدام TypeScript
البرمجة غير المتزامنة هي أسلوب لكتابة الكود حيث لا تؤدي العمليات التي تستغرق وقتًا (مثل طلبات الخادم، قراءة الملفات، المؤقتات) إلى حظر تنفيذ المسار الرئيسي للبرنامج. في تطوير الويب الحديث، البرمجة غير المتزامنة ليست مجرد راحة، بل ضرورة. JavaScript و TypeScript، كونهما لغتين أحاديتي الخيط، تستخدمان حلقة الأحداث (Event Loop) لمعالجة العمليات غير المتزامنة.
يضيف TypeScript إلى هذا النموذج القوي الكتابة الثابتة (Static Typing)، مما يجعل الكود أكثر قابلية للتنبؤ وأمانًا. في هذا الدليل، سنقوم بتحليل جميع جوانب البرمجة غير المتزامنة: من دوال الاسترجاع (Callbacks) الكلاسيكية إلى async/await الحديثة مع الكتابة الكاملة.
أساسيات عدم التزامن: من دوال الاسترجاع إلى الوعود (Promises)
مشكلة دوال الاسترجاع و"جحيم دوال الاسترجاع"
في البداية، تم تنفيذ البرمجة غير المتزامنة في JavaScript من خلال دوال الاسترجاع (Callback functions). لكن هذا النهج سرعان ما أدى إلى كود غير قابل للقراءة:
function loadData(callback: (data: string) => void) { setTimeout(() => { callback("تم تحميل البيانات"); }, 1000);}
loadData((data) => { console.log(data); // استدعاء متداخل loadData((data2) => { console.log(data2); });});من الصعب تصحيح مثل هذا الكود وصيانته. لا يحل TypeScript مشكلة التداخل، لكنه يساعد في كتابة أنواع معاملات دوال الاسترجاع.
الوعود (Promise) — الخلاص من الجحيم
الوعد (Promise) هو كائن يمكن أن يكون في إحدى الحالات الثلاث: pending (قيد الانتظار)، fulfilled (تم بنجاح) أو rejected (مرفوض مع خطأ). يسمح TypeScript بكتابة نوع البيانات التي سيعيدها الوعد بشكل صارم:
function fetchUser(id: number): Promise<User> { return new Promise((resolve, reject) => { setTimeout(() => { if (id > 0) { resolve({ id, name: "إيفان بيتروف" }); } else { reject(new Error("معرف غير صحيح")); } }, 500); });}
interface User { id: number; name: string;}
// الاستخدامfetchUser(1).then(user => { console.log(user.name); // TypeScript يعرف أن user هو من نوع User}).catch(error => { console.error(error.message);});بفضل النوع العام Promise<User>، يضمن مترجم TypeScript أننا داخل .then() سنحصل على كائن من هذا النوع بالضبط.
Async/Await: البرمجة غير المتزامنة الأنيقة
الصياغة والكتابة (Typing)
تسمح الكلمات المفتاحية async و await بكتابة كود غير متزامن يبدو وكأنه كود متزامن. الدالة المميزة بـ async تعيد دائمًا وعدًا (Promise):
async function getGreeting(name: string): Promise<string> { return `مرحبًا، ${name}!`;}
// نوع القيمة المُعادة هو Promise<string>const result: Promise<string> = getGreeting("العالم");من المهم أن تتذكر: إذا كانت الدالة async لا تعيد وعدًا، فإن TypeScript يقوم تلقائيًا بتغليفها في وعد.
مثال مع منطق حقيقي
لننظر إلى سيناريو نموذجي — الحصول على بيانات من API ومعالجتها:
interface Post { userId: number; id: number; title: string; body: string;}
async function fetchPosts(userId: number): Promise<Post[]> { const response = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`); if (!response.ok) { throw new Error(`خطأ HTTP: ${response.status}`); } const data: Post[] = await response.json(); return data;}
async function displayPosts() { try { const posts = await fetchPosts(1); console.log(`تم الحصول على ${posts.length} منشور:`); posts.forEach(post => console.log(`- ${post.title}`));
```