التزامن وتعدد الخيوط في TypeScript: من الألف إلى الياء
في عالم تطوير الويب الحديث، أصبح أداء التطبيقات في المقدمة. TypeScript، كونه امتدادًا صارمًا في كتابة الأنواع فوق JavaScript، يوفر للمطورين أدوات قوية للعمل مع عدم التزامن وتعدد الخيوط. في هذه المقالة، سنقوم بتحليل المفاهيم الأساسية للتزامن، ونتعرف على كيفية عمل Event Loop، ونتعلم استخدام Worker Threads لتعدد الخيوط الحقيقي في Node.js.
1. عدم التزامن و Event Loop: أساس التزامن في TypeScript
قبل الحديث عن تعدد الخيوط، من المهم فهم كيف يعالج TypeScript (و JavaScript) المهام المتزامنة. في الأساس، يوجد Event Loop — وهي آلية تسمح بتنفيذ عمليات الإدخال/الإخراج غير المحظورة، دون إنشاء خيوط جديدة.
لنلقِ نظرة على مثال بسيط لكود غير متزامن:
async function fetchData(url: string): Promise<string> { const response = await fetch(url); return response.text();}
async function main(): Promise<void> { console.log('بدء التحميل'); const data = await fetchData('https://api.example.com/data'); console.log('تم استلام البيانات:', data);}
main();console.log('الكود يستمر في التنفيذ');في هذا المثال، await لا يحجب الخيط الرئيسي. أثناء انتظار الرد من الخادم، يستمر Event Loop في معالجة المهام الأخرى، مثل تنفيذ console.log. هذا هو التزامن — القدرة على معالجة عدة مهام في فترة زمنية واحدة، عن طريق التبديل بينها.
2. الوعود (Promises) و async/await: إدارة العمليات غير المتزامنة
يوفر TypeScript تراكيب نحوية مريحة للعمل مع عدم التزامن. الوعود (Promises) هي كائنات تمثل النتيجة المستقبلية لعملية غير متزامنة. مجموعة async/await تجعل الكود خطيًا وقابلاً للقراءة.
لنلقِ نظرة على سيناريو أكثر تعقيدًا — التنفيذ المتوازي لعدة طلبات:
async function loadMultipleUsers(userIds: number[]): Promise<User[]> { const promises = userIds.map(id => fetch(`/users/${id}`).then(res => res.json())); return Promise.all(promises);}
// الاستخدامconst users = await loadMultipleUsers([1, 2, 3, 4, 5]);console.log(users); // تم تحميل جميع المستخدمين بشكل متوازيطريقة Promise.all تشغل جميع الوعود في وقت واحد، مما يسرع التنفيذ بشكل كبير مقارنة بالتحميل التسلسلي. هذا مثال مثالي على التزامن: يتم تنفيذ المهام "في وقت واحد" ضمن خيط واحد.
3. تعدد الخيوط الحقيقي: Worker Threads في Node.js
على الرغم من أن عدم التزامن يتعامل بشكل ممتاز مع عمليات الإدخال/الإخراج، إلا أنه لا يساعد في المهام كثيفة الاستخدام لوحدة المعالجة المركزية (CPU) (معالجة الصور، الحسابات المعقدة، التشفير). في مثل هذه الحالات، توجد في Node.js Worker Threads — وهي خيوط حقيقية لنظام التشغيل.
لنلقِ نظرة على مثال لحساب المضروب (factorial) في خيط منفصل:
// worker.tsimport { parentPort } from 'worker_threads';
function factorial(n: number): number { if (n === 0) return 1; return n * factorial(n - 1);}
parentPort?.on('message', (n: number) => { const result = factorial(n); parentPort?.postMessage(result);});
// main.tsimport { Worker } from 'worker_threads';
function runWorker(n: number): Promise<number> { return new Promise((resolve, reject) => { const worker = new Worker('./worker.ts'); worker.postMessage(n); worker.on('message', resolve); worker.on('error', reject); });}
async function main() { const result = await runWorker(20); console.log('مضروب 20:', result);}
main();كل Worker يعمل في خيط خاص به، وله Event Loop الخاص به ويتم