تحسين أداء تطبيقات JavaScript: دليل عملي

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

تحسين أداء تطبيقات JavaScript: عمليًا



أداء تطبيقات JavaScript ليس مجرد ميزة إضافية، بل هو عامل حاسم يؤثر على تجربة المستخدم (UX)، ومعدل التحويل، وتكاليف البنية التحتية. قد يكلفك بطء تحميل الصفحة أو تأخير استجابة API ما يصل إلى 40% من حركة المرور. في هذه المقالة، سنستعرض طرقًا عملية لتحسين كود JavaScript (في بيئة Node.js) ستساعدك على جعل تطبيقك أسرع وأكثر كفاءة. لن نتعمق في الأسس النظرية لـ Event Loop، بل سننتقل مباشرة إلى التقنيات المحددة مع الأمثلة.



1. التخزين المؤقت: تقليل الحمل على الخادم



من أكثر الطرق فعالية لتسريع التطبيق هي تجنب إعادة تنفيذ العمليات المكلفة. يتيح لك التخزين المؤقت حفظ نتيجة العمليات الحسابية أو استعلامات قاعدة البيانات وإعادتها عند الطلب المتكرر.



التخزين المؤقت في الذاكرة باستخدام Map

للسيناريوهات البسيطة حيث لا تتطلب البيانات منطق إبطال معقد، فإن الكائن المدمج Map مثالي. إنه أسرع من Object وأكثر ملاءمة للإضافة والحذف المتكرر للمفاتيح.



const cache = new Map();

async function fetchUserData(userId) { if (cache.has(userId)) { console.log('جلب من التخزين المؤقت'); return cache.get(userId); }

// محاكاة استعلام مكلف لقاعدة البيانات const data = await new Promise((resolve) => { setTimeout(() => resolve({ id: userId, name: `User_${userId}` }), 1000); });

cache.set(userId, data); return data;}

// الاستدعاء الأول (انتظار 1 ثانية)console.time('first');await fetchUserData(1);console.timeEnd('first');

// الاستدعاء الثاني (فوري)console.time('second');await fetchUserData(1);console.timeEnd('second');


التخزين المؤقت مع وقت انتهاء الصلاحية (TTL)

لمنع التخزين المؤقت من أن يصبح قديمًا، نحتاج إلى إضافة وقت حياة للسجل. لننفذ تخزينًا مؤقتًا بسيطًا مع TTL.



class TTLMap {  constructor(ttlMs = 60000) {    this.ttl = ttlMs;    this.store = new Map();  }

set(key, value) { const record = { value, expiry: Date.now() + this.ttl }; this.store.set(key, record); }

get(key) { const record = this.store.get(key); if (!record) return null; if (Date.now() > record.expiry) { this.store.delete(key); return null; } return record.value; }}

const userCache = new TTLMap(5000); // تخزين مؤقت لمدة 5 ثوانٍ


2. المعالجة غير المتزامنة وتحسين Event Loop



Node.js أحادي الخيط، لكن طبيعته غير المتزامنة تسمح بمعالجة فعالة للعديد من الطلبات. العدو الرئيسي للأداء هو العمليات المتزامنة التي تحجب Event Loop.



تجنب الإصدارات المتزامنة من الدوال

الوحدات fs و crypto وغيرها لها نظائر متزامنة (readFileSync، crypto.pbkdf2Sync). استخدمها فقط أثناء تهيئة التطبيق. داخل معالجات الطلبات، استخدم فقط الإصدارات غير المتزامنة.



// سيئ: يحجب Event Loopconst data = fs.readFileSync('/path/to/file.txt', 'utf-8');

// جيد: غير متزامنconst data = await fs.promises.readFile('/path/to/file.txt', 'utf-8');


تقسيم المهام الثقيلة باستخدام setImmediate

إذا كان لديك حلقة متزامنة ثقيلة (مثل معالجة مصفوفة من 10 ملايين عنصر)، فستحجب الخادم لعدة ثوانٍ. قسم المهمة إلى أجزاء واستخدم setImmediate لإعطاء Event Loop فرصة لمعالجة الطلبات الأخرى بين الأجزاء.



function processLargeArray(items, chunkSize = 1000) {  let index = 0;

function processChunk() { const chunk = items.slice(index, index + chunkSize); // معالجة الجزء chunk.forEach(item => heavyOperation(item));

index += chunkSize; if (index < items.length) { // الاستمرار في التكرار التالي لـ Event Loop

المدونات

توصيات الكتب