معالجة الأخطاء في لغة JavaScript


معالجة الأخطاء في لغة JavaScript

يا هلا بالشباب، اليوم بنغوص في موضوع مهم جدًا لأي مبرمج جافاسكريبت: معالجة الأخطاء. صدقني، بدونها شغلك بيصير فوضى وبيتكسر بأي وقت. خلونا ندخل بالموضوع على طول.

ليش نحتاج معالجة الأخطاء؟

بكل بساطة، عشان برنامجك ما يوقف فجأة ويطلع لك وجه حزين للمستخدم. الأخطاء بتحصل، سواء كانت من المستخدم، الشبكة، أو حتى غلطة برمجية منك. مهمتنا إننا نتعامل معها بأناقة ونعطي المستخدم تجربة كويسة حتى لو صار خطأ.

الـ try...catch بلوك: صديقك الوفي

هذا هو الأساس يا جماعة. أي كود تتوقع إنه ممكن يرمي خطأ، حطه داخل الـ try بلوك. وإذا صار الخطأ، الـ catch بلوك بيمسك الخطأ وبيعالج الموضوع.

ملاحظة: الـ catch بلوك يستقبل باراميتر (عادةً نسميه error أو err) فيه تفاصيل الخطأ اللي صار. استفيد منه عشان تعرف وش المشكلة بالضبط.

شوف المثال:

try {
        // الكود اللي ممكن يسبب خطأ
        let result = someUndefinedVariable * 2;
        console.log(result); // هذا السطر ما راح يتنفذ لو صار خطأ فوق
    } catch (error) {
        // هنا نعالج الخطأ
        console.error("يا ساتر! صار خطأ:", error.message);
        // ممكن تعرض رسالة للمستخدم، تسجل الخطأ، أو تسوي أي شيء ثاني
    }

    console.log("البرنامج استمر في العمل بعد معالجة الخطأ.");

الـ finally بلوك: دائمًا يتنفذ

الـ finally بلوك يتنفذ دائمًا، سواء صار خطأ أو ما صار. مفيد جدًا لو عندك موارد لازم تقفلها، أو تنظيف معين لازم تسويه، بغض النظر عن نتيجة الـ try بلوك.

function processFile() {
        let fileHandle;
        try {
            // نفترض هنا إننا نفتح ملف
            fileHandle = openFile("mydata.txt");
            // نسوي عمليات على الملف
            console.log("الملف مفتوح وجاهز للعمل.");
            // نفترض هنا خطأ
            throw new Error("صار خطأ أثناء معالجة الملف!");
        } catch (error) {
            console.error("خطأ:", error.message);
        } finally {
            // هذا الجزء يتنفذ دائمًا، سواء صار خطأ أو لا
            if (fileHandle) {
                closeFile(fileHandle); // نتأكد إن الملف يتقفل
                console.log("الملف تم إغلاقه.");
            }
        }
    }

    // هنا وظائف وهمية لتوضيح الفكرة
    function openFile(filename) {
        console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">فتح الملف: ${filename}</code>);
        return { name: filename, status: "open" };
    }

    function closeFile(handle) {
        console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">إغلاق الملف: ${handle.name}</code>);
        handle.status = "closed";
    }

    processFile();

رمي الأخطاء بنفسك (throw)

تقدر ترمي أخطاء بنفسك باستخدام الكلمة المفتاحية throw. هذا مفيد لما تكتشف إن فيه مشكلة معينة في منطق برنامجك وما تبغى الكود يكمل تنفيذ بشكل خاطئ.

يفضل دائمًا ترمي كائن Error أو كائن مشتق منه، لأنه بيعطيك معلومات مفيدة زي رسالة الخطأ ومسار التنفيذ (stack trace).

function divide(a, b) {
        if (b === 0) {
            throw new Error("يا صاحبي! ما تقدر تقسم على صفر.");
        }
        return a / b;
    }

    try {
        console.log(divide(10, 2)); // 5
        console.log(divide(10, 0)); // هذا بيرمي خطأ
    } catch (error) {
        console.error("خطأ في عملية القسمة:", error.message);
    }

معالجة الأخطاء في العمليات غير المتزامنة (Asynchronous)

الأخطاء في الـ Promises والـ async/await لها طريقة خاصة في المعالجة. لا تحاول تستخدم try...catch عادي مع Promises بدون async/await، ما راح يشتغل صح.

مع الـ Promises (.catch())

الـ Promise له ميثود خاصة اسمها .catch() عشان تمسك الأخطاء اللي تصير فيه.

function fetchData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const success = false; // نفترض هنا إن فيه خطأ صار
                if (success) {
                    resolve("تم جلب البيانات بنجاح!");
                } else {
                    reject("فشل في جلب البيانات من السيرفر.");
                }
            }, 1000);
        });
    }

    fetchData()
        .then(data => console.log(data))
        .catch(error => console.error("يا ليتني ما سويت الطلب:", error));

مع الـ async/await (try...catch)

هنا نقدر نرجع لـ try...catch اللي نعرفه، لأنه الـ await بيخلي الكود يتصرف كأنه متزامن.

async function getDataUsingAsyncAwait() {
        try {
            const data = await fetchData(); // نفس الـ fetchData اللي فوق
            console.log("البيانات اللي جاتني:", data);
        } catch (error) {
            console.error("يا كثر الأخطاء! فيه مشكلة في جلب البيانات:", error);
        } finally {
            console.log("انتهينا من محاولة جلب البيانات.");
        }
    }

    getDataUsingAsyncAwait();

أنواع الأخطاء الشائعة

فيه أنواع أخطاء بتشوفها كثير في جافاسكريبت، زي:

  • ReferenceError: لما تحاول تستخدم متغير ما تم تعريفه.
  • TypeError: لما تسوي عملية على نوع بيانات غلط (مثلاً، تستدعي ميثود على null).
  • SyntaxError: خطأ في كتابة الكود نفسه (ما راح يتنفذ أصلاً).
  • RangeError: لما تكون قيمة خارج النطاق المسموح (مثلاً، مصفوفة بطول سالب).

نصائح من أخوك السعودي

  1. لا تبلع الأخطاء: لا تسوي catch وتخلي الـ catch بلوك فاضي. هذا أسوأ شيء ممكن تسويه. لازم تسجل الخطأ أو تتعامل معه بطريقة صحيحة.
  2. رسائل واضحة: لما ترمي خطأ أو تسجله، خلي الرسالة واضحة ومفيدة. بتشكر نفسك عليها بعدين لما تحاول تصلح المشكلة.
  3. لا تعرض الأخطاء الخام للمستخدم: المستخدم ما يهمه الـ stack trace. اعرض له رسالة سهلة ومفهومة زي "حدث خطأ غير متوقع، الرجاء المحاولة مرة أخرى."
  4. استخدم أدوات التسجيل (Logging): في بيئة الإنتاج، لا تعتمد على console.error وبس. استخدم مكتبات تسجيل احترافية ترسل الأخطاء لسيرفرات خاصة عشان تقدر تتابعها وتحللها.
  5. فكر في السيناريوهات: قبل لا تكتب الكود، فكر وش ممكن يصير غلط؟ وكيف برنامجك بيتعامل مع هالشيء؟

وبكذا نكون غطينا أساسيات معالجة الأخطاء في جافاسكريبت. طبقوا اللي تعلمتوه، وصدقوني شغلكم بيصير أنظف وأقوى. بالتوفيق يا أبطال!