عندما يتطلب الموقع "الحي" كوداً حياً
\nتخيل: أنت تكتب تطبيق مراسلة باستخدام TypeScript. يرسل المستخدم رسالة — ويجب أن يراها الطرف الآخر فوراً، دون تحديث الصفحة. أو لوحة بورصة حيث يتغير سعر الأسهم كل ثانية. أو مجرد خلاصة إشعارات تُحدث نفسها بنفسها. كيف يمكن تحقيق ذلك؟ هناك مرشحان رئيسيان — long polling و WebSocket. لكن أيهما تختار حتى لا تفسد الأداء أو تكسر تجربة المستخدم؟
\nفي هذه المقالة، سنشرح من خلال أمثلة حقيقية بلغة TypeScript كيفية عمل كل تقنية، وأين تتألق، وأين تخذل. ستحصل ليس فقط على النظرية، بل أيضاً على أجزاء جاهزة من الكود لـ Node.js والمتصفح. هيا بنا!
\n\nما هو Long Polling في TypeScript وكيف يعمل
\nLong polling هو تطور لطلب HTTP العادي. يرسل العميل طلباً إلى الخادم، لكن الخادم لا يرد فوراً إذا لم تكن هناك بيانات. إنه يبقي الاتصال مفتوحاً حتى يظهر حدث جديد. بمجرد أن تصبح البيانات جاهزة — يرسل الخادم الرد، ويعالجه العميل، ثم ينشئ على الفور طلباً جديداً. تتكرر الدورة.
\nيبدو هذا كحل مؤقت؟ نوعاً ما. لكنه يعمل حتى عبر أكثر البروكسيات المؤسسية تشدداً التي تحظر WebSocket.
\n\nتنفيذ Long Polling بلغة TypeScript (جانب الخادم)
\nسنكتب خادماً بسيطاً باستخدام Express مع TypeScript. سيحاكي قائمة انتظار للرسائل.
\nimport express, { Request, Response } from 'express';\nimport cors from 'cors';\n\nconst app = express();\napp.use(cors());\napp.use(express.json());\n\n// تخزين الرسائل (محاكاة قائمة انتظار)\nconst messages: string[] = [];\n\n// نقطة نهاية لـ long polling\napp.get('/poll', (req: Request, res: Response) => {\n // انتظار رسالة جديدة لمدة تصل إلى 30 ثانية\n const checkForMessage = () => {\n if (messages.length > 0) {\n // إرسال أول رسالة وحذفها من قائمة الانتظار\n const message = messages.shift();\n res.json({ data: message, timestamp: Date.now() });\n } else {\n // إذا لم تكن هناك رسائل — استمر في الانتظار\n setTimeout(checkForMessage, 1000); // تحقق كل ثانية\n }\n };\n\n // مهلة في حال عدم وجود رسائل لفترة طويلة\n const timeout = setTimeout(() => {\n res.json({ data: null, timeout: true });\n }, 30000);\n\n // إذا قطع العميل الاتصال — نظف المؤقتات\n req.on('close', () => {\n clearTimeout(timeout);\n });\n\n checkForMessage();\n});\n\n// نقطة نهاية لإرسال رسالة\napp.post('/send', (req: Request, res: Response) => {\n const { message } = req.body;\n if (message) {\n messages.push(message);\n res.status(200).json({ status: 'ok' });\n } else {\n res.status(400).json({ error: 'message is required' });\n }\n});\n\napp.listen(3000, () => console.log('Long poll server on port 3000'));\n\nالجزء العميل بلغة TypeScript (المتصفح)
\nasync function startLongPolling() {\n try {\n const response = await fetch('http://localhost:3000/poll');\n const data = await response.json();\n\n if (data.data) {\n console.log('رسالة جديدة:', data.data);\n // تحديث واجهة المستخدم\n updateUI(data.data);\n }\n\n // بدء poll جديد فوراً\n startLongPolling();\n } catch (error) {\n console.error('خطأ في long polling:', error);\n // انتظر ثانيتين وحاول مرة أخرى\n setTimeout(startLongPolling, 2000);\n }\n}\n\nfunction updateUI(message: string) {\n // الكود الخاص بك لتحديث DOM\n const container = document.getElementById('messages');\n if (container) {\n const item = document.createElement('li');\n item.textContent = message;\n container.appendChild(item);\n }\n}\n\n// البدء عند تحميل الصفحة\ndocument.addEventListener('DOMContentLoaded', startLongPolling);\nالخلاصة: long polling بسيط في التنفيذ ولا يتطلب بروتوكولات خاصة. لكن كل طلب هو عبارة عن رؤوس HTTP، وملفات تعريف ارتباط، وتكاليف إضافية. مع 1000 عميل متزامن