البرمجة غير المتزامنة في TypeScript: دليل شامل للمبتدئين
\n\nالبرمجة غير المتزامنة هي أحد المفاهيم الأساسية في تطوير الويب الحديث. إذا كنت قد بدأت للتو في تعلم TypeScript، فإن فهم كيفية العمل مع الكود غير المتزامن سيفتح أمامك إمكانيات لإنشاء تطبيقات سريعة وسريعة الاستجابة. في هذا الدليل، سنشرح أساسيات البرمجة غير المتزامنة في TypeScript: من الاستدعاءات البسيطة (callbacks) إلى async/await الحديثة، مع التركيز على الكتابة الصارمة للأنواع التي يوفرها TypeScript.
ما هي البرمجة غير المتزامنة ولماذا نحتاجها؟
\n\nفي البرمجة المتزامنة، يتم تنفيذ الكود بشكل تسلسلي: كل عملية تالية تنتظر انتهاء العملية السابقة. قد يؤدي ذلك إلى "تجميد" الواجهة إذا كنت بحاجة، على سبيل المثال، إلى تحميل بيانات من الخادم. تسمح البرمجة غير المتزامنة بتشغيل العمليات الطويلة (طلبات API، قراءة الملفات، المؤقتات) في الخلفية، دون حظر تيار التنفيذ الرئيسي. TypeScript، كونه امتدادًا لـ JavaScript، يدعم بشكل كامل الأنماط غير المتزامنة ويضيف إليها نظام أنواع قوي.
\n\nPromise: أساس البرمجة غير المتزامنة في TypeScript
\n\nPromise هو كائن يمثل نتيجة عملية غير متزامنة، ويمكن أن يكون في إحدى ثلاث حالات: pending (قيد الانتظار)، fulfilled (تم التنفيذ بنجاح) أو rejected (تم التنفيذ مع خطأ). في TypeScript، يمكننا تحديد نوع البيانات التي سيعيدها الـ Promise، مما يجعل الكود أكثر قابلية للتنبؤ.
مثال على إنشاء Promise مع كتابة الأنواع:
\n\n// دالة تعيد Promise من نوع string\nfunction fetchUserData(userId: number): Promise<string> {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n if (userId > 0) {\n resolve(`بيانات المستخدم بالمعرف ${userId}`);\n } else {\n reject(new Error("معرف مستخدم غير صحيح"));\n }\n }, 1000);\n });\n}\n\n// استخدام Promise\nfetchUserData(123)\n .then((data: string) => {\n console.log(data); // "بيانات المستخدم بالمعرف 123"\n })\n .catch((error: Error) => {\n console.error(error.message);\n });\n\nلاحظ: لقد حددنا بوضوح أن الـ Promise يعيد string، وفي .then() المعامل data له أيضًا النوع string. يساعد هذا في تجنب الأخطاء في مرحلة التجميع.
سلاسل Promise ومعالجة الأخطاء
\n\nيمكن ربط الـ Promises في سلاسل، لنقل نتيجة عملية غير متزامنة إلى أخرى. سيقوم TypeScript تلقائيًا باستنتاج الأنواع في كل خطوة.
\n\nمثال على سلسلة Promise:
\n\ninterface User {\n id: number;\n name: string;\n}\n\nfunction getUser(id: number): Promise<User> {\n return new Promise((resolve) => {\n setTimeout(() => resolve({ id, name: "آنا" }), 500);\n });\n}\n\nfunction getPosts(userId: number): Promise<string[]> {\n return new Promise((resolve) => {\n setTimeout(() => resolve(["منشور 1", "منشور 2"]), 500);\n });\n}\n\n// سلسلة الاستدعاءات\ngetUser(1)\n .then((user: User) => {\n console.log(`المستخدم: ${user.name}`);\n return getPosts(user.id); // نعيد Promise جديد\n })\n .then((posts: string[]) => {\n console.log(`المنشورات: ${posts.join(", ")}`);\n })\n .catch((error: Error) => {\n console.error("خطأ:", error.message);\n });\n\nإذا حدث خطأ في أي مرحلة، سينتقل التنفيذ إلى أقرب .catch(). هذه معالجة مركزية للأخطاء - وهي إحدى المزايا الرئيسية للـ Promises مقارنة بالاستدعاءات (callbacks).
Async/await: بناء جملة أنيق للبرمجة غير المتزامنة
\n\nبناء جملة async/await يجعل الكود غير المتزامن يبدو وكأنه متزامن، مما يبسط قراءته