البرمجة غير المتزامنة في TypeScript: دليل شامل للمبتدئين
تطوير الويب الحديث لا يمكن أن يتم دون فهم البرمجة غير المتزامنة. TypeScript، كطبقة علوية فوق JavaScript، يوفر أدوات قوية للعمل مع الكود غير المتزامن، مما يجعله أكثر أمانًا وقابلية للتنبؤ. في هذا الدليل، سنقوم بتحليل المفاهيم الأساسية للبرمجة غير المتزامنة في TypeScript، بدءًا من الأساسيات وصولاً إلى الأمثلة العملية.
ما هي البرمجة غير المتزامنة ولماذا نحتاجها؟
في البرمجة المتزامنة، يتم تنفيذ الكود بشكل تسلسلي صارم: كل عملية تالية تنتظر انتهاء العملية السابقة. هذا يخلق مشاكل عند التعامل مع المهام الطويلة، مثل طلبات الخادم، قراءة الملفات، أو المؤقتات. البرمجة غير المتزامنة تسمح بتنفيذ هذه المهام في الخلفية، دون حظر تيار التنفيذ الرئيسي.
يضيف TypeScript إلى JavaScript غير المتزامن كتابة صارمة، مما يساعد في اكتشاف الأخطاء في مرحلة التجميع. على سبيل المثال، تسمح Promise المكتوبة بمعرفة نوع البيانات الذي ستعيده الدالة غير المتزامنة بدقة.
// كود متزامن - يحظر التنفيذ
function fetchData(): string {
// عملية طويلة
return "بيانات";
}
// كود غير متزامن - لا يحظر
async function fetchDataAsync(): Promise<string> {
const response = await fetch('/api/data');
return response.json();
}
الأساسيات: Callback و Promise و Async/Await
تاريخيًا، بدأت البرمجة غير المتزامنة في JavaScript مع دوال الاسترجاع (callback functions)، التي كانت تُمرر كوسائط. لكن هذا الأسلوب أدى إلى "جحيم الاسترجاعات" — هياكل متداخلة بعمق، يصعب فهمها وتصحيحها.
Promises (الوعود) حلت هذه المشكلة من خلال توفير صيغة تسلسلية. Promise هو كائن يمثل نتيجة عملية غير متزامنة، ويمكن أن يكون في إحدى ثلاث حالات: pending (قيد الانتظار)، fulfilled (تم التنفيذ)، أو rejected (مرفوض).
// مثال على Promise في TypeScript
function loadUser(id: number): Promise<User> {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "Alice" });
} else {
reject(new Error("معرف مستخدم غير صالح"));
}
}, 1000);
});
}
// الاستخدام
loadUser(1)
.then(user => console.log(user))
.catch(error => console.error(error));
Async/Await هو سكر نحوي فوق Promises، يجعل الكود غير المتزامن يبدو وكأنه متزامن. الكلمة المفتاحية async قبل الدالة تشير إلى أنها تعيد Promise، و await توقف التنفيذ حتى الحصول على النتيجة.
// Async/Await في TypeScript
async function getUserData(): Promise<void> {
try {
const user = await loadUser(1);
console.log(user.name); // "Alice"
} catch (error) {
console.error("خطأ في التحميل:", error);
}
}
getUserData();
كتابة الدوال غير المتزامنة في TypeScript
واحدة من المزايا الرئيسية لـ TypeScript هي الكتابة الصارمة. بالنسبة للدوال غير المتزامنة، من المهم تحديد نوع Promise الذي تعيده بشكل صحيح. إذا كانت الدالة تعيد سلسلة نصية، سيكون النوع Promise<string>. سيتحقق مترجم TypeScript من أنك لا تحاول استخدام النتيجة قبل الحصول عليها.
لننظر إلى مثال مع طلب API مكتوب:
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function fetchTodo(id: number): Promise<Todo> {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
if (!response.ok) {
throw new Error(`خطأ HTTP! الحالة: ${response.status}`);
}
return await response.json() as Todo;
}
// الاستخدام مع الكتابة
async function displayTodo(): Promise<void> {
const todo = await fetchTodo
```