عملية تتبع أخطاء البرامج لمعرفة مصدر المشكلة في نود Node.js خلال مرحلة التطوير توفر على المطور الكثير من وقت تطوير المشروع، وتزداد صعوبة تلك المهمة مع كبر حجم المشروع وزيادة تعقيده، وهنا يأتي دور مُنقِّح الأخطاء debugger ليساعد في ذلك، وهو برنامج يسمح للمطور بمعاينة البرنامج أثناء تشغيله عبر تنفيذ الشيفرة سطرًا تلو الآخر ومعاينة حالة التطبيق وتغيرها، مما يوفر للمبرمج نظرة أقرب على طريقة عمل البرنامج ما يسهل العثور على الأخطاء وإصلاحها.
وعادة ما يضيف المطورون تعليمات الطباعة داخل شيفرة البرنامج لمعاينة بعض القيم أثناء تشغيله، حيث يضيف المطور في نود تعليمات طباعة مثل console.log()
و console.debug()
، ومع أن هذه الطريقة سهلة وسريعة لكنها تبقى يدوية ولا تخدم دومًا في الحالات المعقدة أو عندما يكون التطبيق كبيرًا، فقد ينسى أحيانًا المطور بعض تعليمات الطباعة تلك ما قد يؤدي لطباعة معلومات خاصة وحساسة عن التطبيق يجعله عرضة للاختراق، وهنا يوفر لنا المنقح طريقة أفضل لمراقبة البرنامج أثناء التشغيل دون أن يُعرّض البرنامج لمثل تلك الأخطار.
وأهم ميزتين في منقح الأخطاء الداخلي هما مراقبة الكائنات، وإضافة نقاط الوقوف breakpoints، حيث تتيح مراقبة الكائنات طريقة لمشاهدة التغير في حالة المتغيرات أثناء تنفيذ البرنامج خطوة بخطوة، أما نقاط الوقوف فهي أماكن ضمن الشيفرة يمكن للمبرمج تحديدها ليتوقف البرنامج عن التنفيذ مؤقتًا عند الوصول إليها، ليعطي فرصة للمبرمج لمعاينة حالة البرنامج في تلك اللحظة.
سنتعلم في هذا المقال طريقة استخدام المنقح لاستكشاف الأخطاء ضمن بعض البرامج في نود، حيث سنستخدم بدايةً أداة تنقيح الأخطاء الداخلية في نود ونتعلم طريقة إعداد المراقبة للمتغيرات وإضافة نقاط التوقف لنتمكن من اكتشاف المشاكل وإصلاحها، ثم سنتعلم استخدام واجهة أداة المطور في متصفح جوجل كروم بدلًا من التعامل مع المنقح من سطر الأوامر.
المستلزمات
هذا المقال جزء من سلسلة دليل تعلم Node.js لذا يجب قبل قراءته:
- تثبيت بيئة Node.js على الجهاز، حيث استخدمنا في هذا المقال الإصدار رقم 10.19.0.
- معرفة بأساسيات جافاسكربت والتعامل مع الدوال.
- تثبيت متصفح جوجل كروم أو متصفح كروميوم مفتوح المصدر.
استخدام الراصدات Watchers مع المنقح Debugger
الميزتين الأساسيتين لمنقح الأخطاء هما مراقبة المتغيرات وتغير قيمها أثناء التنفيذ، وميزة الإيقاف المؤقت لعمل البرنامج عند أماكن محددة من الشيفرة باستخدام نقاط الوقوف، وسنتعلم في هذه الفقرة طريقة مراقبة المتغيرات لتساعدنا في اكتشاف الأخطاء.
تساعدنا عملية مراقبة المتغيرات ورصدها في فهم كيفية تغير قيم تلك المتغيرات أثناء تنفيذ البرنامج، وسنستفيد من هذه الميزة في اكتشاف الأخطاء في منطق عمل البرنامج وإصلاحها، وسنبدأ بإنشاء مجلد جديد بالاسم debugging سيحوي على البرامج التي سنتعامل معها:
$ mkdir debugging
وندخل إلى المجلد:
$ cd debugging
ننشئ داخله ملف جافاسكربت جديد بالاسم badLoop.js ونفتحه ضمن أي محرر نصوص، حيث سنستخدم في أمثلتنا محرر نانو nano كالتالي:
$ nano badLoop.js
سنكتب برنامجًا يمر على عناصر المصفوفة ويجمع قيمها لحساب المجموع الكلي لها، حيث تمثل تلك الأرقام عدد الطلبات اليومي لمتجر خلال فترة أسبوع، حيث سيطبع البرنامج المجموع الكلي للأرقام في تلك المصفوفة، ليكون البرنامج كالتالي:
let orders = [341, 454, 198, 264, 307]; let totalOrders = 0; for (let i = 0; i <= orders.length; i++) { totalOrders += orders[i]; } console.log(totalOrders);
أنشأنا بداية مصفوفة الطلبات orders
والتي تحوي خمسة أعداد، ثم أنشأنا متغير المجموع الكلي للطلبات totalOrders
وضبطنا قيمته الأولية إلى الصفر 0
، حيث سنخزن ضمنه المجموع الكلي للأرقام السابقة، ومررنا ضمن حلقة for
على عناصر المصفوفة orders
وأضفنا كل قيمة منها إلى متغير المجموع الكلي totalOrders
، ثم أخيرًا طبعنا قيمة المجموع الكلي.
والآن نحفظ الملف ونخرج منه وننفذ البرنامج ونعاين النتيجة:
$ node badLoop.js
يظهر لنا الخرج التالي:
NaN
القيمة NaN
في جافاسكربت هي اختصار لجملة "ليس عددًا" أو "Not a Number"، ولكن كيف حصلنا على تلك القيمة مع أن المصفوفة لا تحوي سوى قيم عددية؟ الطريقة الأفضل لمعرفة سبب المشكلة هي استخدام منقح الأخطاء، وهنا سنبدأ بالتعرف على منقح نود ونستخدمه رصد قيمة كل من المتغيرين totalOrders
و i
ضمن حلقة for
، ولتشغيله نضيف خيار inspect
قبل اسم الملف عند تشغيله بواسطة الأمر node
كالتالي:
$ node inspect badLoop.js
سنلاحظ ظهور الخرج التالي:
< Debugger listening on ws://127.0.0.1:9229/e1ebba25-04b8-410b-811e-8a0c0902717a < For help, see: https://nodejs.org/en/docs/inspector < Debugger attached. Break on start in badLoop.js:1 > 1 let orders = [341, 454, 198, 264, 307]; 2 3 let totalOrders = 0;
يحتوي السطر الأول من الخرج على رابط خادم تنقيح الأخطاء، حيث تستفيد منه أدوات تنقيح الأخطاء الخارجية مثل متصفح الويب للتواصل مع خادم التنقيح الخاص بنود وهو ما سنتعامل معه لاحقًا، وافتراضيًا يكون هذا الخادم متاحًا على المنفذ :9229
والعنوان المحلي localhost
أو 127.0.0.1
، ويفضل منع الوصول لهذا المنفذ من الشبكة الخارجية والوصول إليه من الجهاز محليًا فقط.
وبعد ربط منقح الأخطاء ستظهر الرسالة Break on start in badLoop.js:1
والتي تعني توقف التنفيذ عند أول سطر من الملف، حيث يمكن وضع نقاط الوقوف ضمن الشيفرة لتحديد مكان توقف التنفيذ وكما لاحظنا فمنقح الأخطاء يتوقف افتراضيًا عند أول سطر من الملف دومًا ويُظهر لنا مقطع من الشيفرة عند مكان التوقف وبعده سطر جديد يبدأ بالكلمة debug
يمكننا كتابة الأوامر ضمنه:
... > 1 let orders = [341, 454, 198, 264, 307]; 2 3 let totalOrders = 0; debug>
نلاحظ وجود الرمز >
بجانب رقم السطر الأول 1
وهو دلالة على مكان توقف التنفيذ الحالي، ويُظهر السطر الأخير استعداد منقح الأخطاء لتلقي الأوامر، حيث يمكننا مثلًا تنفيذ أمر لتوجيهه لتقديم عملية التنفيذ خطوة إلى الأمام والذهاب إلى السطر التالي من التنفيذ، ويمكن إدخال أحد الأوامر التالية:
-
c
أوcont
: لإكمال عملية التنفيذ حتى الوصول إلى نقطة الوقوف التالية أو حتى الانتهاء من تنفيذ البرنامج. -
n
أوnext
: للتقدم خطوة إلى الأمام في التنفيذ إلى السطر التالي من الشيفرة. -
s
أوstep
: للدخول إلى دالة ما، حيث تكون عملية التقدم افتراضيًا ضمن النطاق scope الذي نصحح الأخطاء ضمنه فقط، وتمكننا هذه العملية من الدخول ضمن دالة استدعتها الشيفرة التي نفحصها لمعاينة عملها من الداخل ومراقبة تعاملها مع البيانات المُمررة لها. -
o
: للخروج من دالة حيث سيعود التنفيذ لخارجها إلى مكان استدعائها، وهو المكان الذي ستُرجع قيمة تنفيذ الدالة إليه، حيث يفيد هذا الأمر في العودة مباشرةً إلى خارج الدالة إلى المكان الذي كنا نعاينه قبل الدخول إليها. -
pause
: لإيقاف التنفيذ مباشرةً مؤقتًا.
لنتقدم بتنفيذ البرنامج سطرًا تلو الآخر بتنفيذ الأمر n
للانتقال إلى السطر التالي:
debug> n
نلاحظ تقدم التنفيذ إلى السطر الثالث:
break in badLoop.js:3 1 let orders = [341, 454, 198, 264, 307]; 2 > 3 let totalOrders = 0; 4 5 for (let i = 0; i <= orders.length; i++) {
يتم تجاوز الأسطر الفارغة، لذا إذا قدّمنا علمية التنفيذ سطرًا آخر الآن بتنفيذ الأمر n
مجددًا سينتقل التنفيذ إلى السطر الخامس:
break in badLoop.js:5 3 let totalOrders = 0; 4 > 5 for (let i = 0; i <= orders.length; i++) { 6 totalOrders += orders[i]; 7 }
يوقف التنفيذ الآن في بداية الحلقة، وإذا كانت الطرفية تدعم إظهار الألوان في الخرج سنلاحظ تحديد القيمة 0
ضمن التعليمة let i = 0
، حيث يحدد المنقح أي قسم من الشيفرة على وشك التنفيذ، ففي الحلقة for
أول ما ينفذ هو إسناد القيمة لعداد الحلقة، وسنبدأ هنا بمعاينة القيم للمتغيرات لنحدد سبب الحصول على القيمة NaN
بدلًا من القيمة العددية لمتغير المجموع totalOrders
، حيث أن قيمتي المتغيرين totalOrders
و i
تتغيران عند كل دورة للحلقة، وسنستفيد من ميزة الرصد والمراقبة التي يوفرها المنقح في مراقبة قيم هذين المتغيرين.
نبدأ بإعداد المراقبة لمتغير المجموع الكلي totalOrders
بتنفيذ التعليمة التالية:
debug> watch('totalOrders')
لمراقبة أي متغير خلال تنقيح الأخطاء نستدعي الدالة watch()
الذي يوفرها المنقح ونمرر لها سلسلة نصية تحوي على اسم المتغير الذي نريد مراقبته، وبعد الضغط على زر الإدخال ENTER وتنفيذ الدالة watch()
سينتقل التنفيذ إلى سطر جديد دون ظهور أي خرج، وستظهر القيم التي نراقبها عند الانتقال للسطر التالي.
لنراقب أيضًا المتغير الآخر i
بنفس الطريقة:
debug> watch('i')
سنشاهد الآن عملية المراقبة للمتغيرات السابقة، ننفذ الأمر n
للانتقال خطوة للأمام وسيظهر لنا التالي:
break in badLoop.js:5 Watchers: 0: totalOrders = 0 1: i = 0 3 let totalOrders = 0; 4 > 5 for (let i = 0; i <= orders.length; i++) { 6 totalOrders += orders[i]; 7 }
نلاحظ ظهور قيم المتغيرين اللذين نراقبهما totalOrders
و i
قبل الشيفرة حيث سيتم تحديث هذه القيم عند تغيرها، ونلاحظ أن المنقح يحدد حاليًا الخاصية length
من التعليمة orders.length
، ما يعني أن الخطوة التالية هي التحقق من شرط إكمال التنفيذ للحلقة قبل إعادة تنفيذ التعليمات في جسم الحلقة، وبعدها ستُنفذ تعليمة زيادة قيمة عداد الحلقة i++
.
والآن نتقدم خطوة للأمام بتنفيذ الأمر n
مجددًا للدخول إلى جسم الحلقة:
break in badLoop.js:6 Watchers: 0: totalOrders = 0 1: i = 0 4 5 for (let i = 0; i <= orders.length; i++) { > 6 totalOrders += orders[i]; 7 } 8
ستُعدِّل التعليمة الحالية من قيمة المتغير totalOrders
، وسنلاحظ ذلك من تغير تلك القيمة ضمن قسم المراقبة في الأعلى.
والآن نتقدم خطوة إلى الأمام بتنفيذ n
ليظهر لنا ما يلي:
Watchers: 0: totalOrders = 341 1: i = 0 3 let totalOrders = 0; 4 > 5 for (let i = 0; i <= orders.length; i++) { 6 totalOrders += orders[i]; 7 }
نلاحظ أن قيمة متغير المجموع الكلي totalOrders
تساوي قيمة أول عنصر من المصفوفة 341
، والخطوة التالية الآن هي التحقق من شرط إكمال تنفيذ الحلقة، لذا ننفذ الأمر n
لتعديل قيمة عداد الحلقة i
:
break in badLoop.js:5 Watchers: 0: totalOrders = 341 1: i = 1 3 let totalOrders = 0; 4 > 5 for (let i = 0; i <= orders.length; i++) { 6 totalOrders += orders[i]; 7 }
إلى الآن قد تقدمنا عدة خطوات يدويًا ضمن الشيفرة لمراقبة التغير في قيم المتغيرات، لكن تلك الطريقة غير عملية حيث سنتعرف في الفقرة التالية على حل لهذه المشكلة باستخدام نقاط الوقوف، وسنُكمل حاليًا العمل بتقديم عملية التنفيذ يدويًا ومراقبة قيم المتغيرات للعثور على سبب المشكلة.
والآن نتقدم في التنفيذ 12 خطوة للأمام لنلاحظ الخرج التالي:
break in badLoop.js:5 Watchers: 0: totalOrders = 1564 1: i = 5 3 let totalOrders = 0; 4 > 5 for (let i = 0; i <= orders.length; i++) { 6 totalOrders += orders[i]; 7 }
عدد القيم ضمن المصفوفة orders
هو خمسة، ولكن قيمة عداد الحلقة i
الحالية هي 5
، وبما أننا نستخدم قيمة المتغير i
للوصول إلى العنصر ضمن المصفوفة بالترتيب الحالي فالقيمة عند الترتيب orders[5]
غير موجودة، وترتيب آخر قيمة ضمن المصفوفة orders
هو 4
، ما يعني أن محاولة الوصول للعنصر السادس باستخدام orders[5]
سيعيد القيمة undefined
.
والآن نتقدم بالتنفيذ خطوة للأمام بتنفيذ الأمر n
:
break in badLoop.js:6 Watchers: 0: totalOrders = 1564 1: i = 5 4 5 for (let i = 0; i <= orders.length; i++) { > 6 totalOrders += orders[i]; 7 } 8
وبالتقدم خطوة إضافية بتنفيذ n
نلاحظ القيمة الجديدة للمتغير totalOrders
:
break in badLoop.js:5 Watchers: 0: totalOrders = NaN 1: i = 5 3 let totalOrders = 0; 4 > 5 for (let i = 0; i <= orders.length; i++) { 6 totalOrders += orders[i]; 7 }
لاحظنا بالاستفادة من عملية تنقيح الشيفرة ومراقبة قيم المتغيرين totalOrders
و i
أن الحلقة تُنفَّذ ستة مرات بدلًا من خمسة، وعندما تكون قيمة عداد الحلقة i
هي 5
فمحاولة الوصول للعنصر الحالي orders[5]
وإضافته للمتغير totalOrders
ستجعل من قيمة المجموع تساوي NaN
، لأن قيمة العنصر السادس orders[5]
الغير موجود ستكون undefined
، فإذًا المشكلة هي في شرط الحلقة for
فبدلًا من التحقق من أن قيمة العداد i
هي أصغر أو تساوي طول المصفوفة orders
يجب أن نتحقق من أنها أصغر من الطول فقط.
وبعد أن حددنا المشكلة نخرج من المنقح ونصحح الخطأ ضمن الشيفرة ونعيد تنفيذ البرنامج ونتحقق من النتيجة، لكن أولًا ننفذ أمر الخروج .exit
ثم نضغط زر الإدخال ENTER:
debug> .exit
نخرج بذلك من وضع المنقح ونعود إلى الملف badLoop.js ونفتحه ضمن محرر النصوص ونعدل شرط حلقة for
كالتالي:
... for (let i = 0; i < orders.length; i++) { ...
نحفظ الملف ونخرج منه ونشغل البرنامج:
$ node badLoop.js
سنلاحظ ظهور قيمة المجموع الصحيحة ونكون بذلك حللنا المشكلة:
1564
نكون بذلك قد تعلمنا طريقة استخدام المنقح ودالة مراقبة المتغيرات watch
الخاصة به لاستكشاف وتحديد الأخطاء أثناء التنفيذ!
وسنتعلم الآن في الفقرة التالية كيف يمكننا الاستفادة من نقاط الوقوف لتنقيح الأخطاء ضمن البرنامج دون الحاجة لتقديم التنفيذ يدويًا سطرًا تلو الآخر.
استخدام نقاط الوقوف Breakpoints
تتألف البرامج في نود عادة من عدة وحدات برمجية يتشابك عملها مع بعضها بعضًا، لذا محاولة تنقيح الأخطاء سطرًا تلو الآخر كما فعلنا في الفقرة السابقة أمر صعب وغير مجدي في التطبيقات الكبيرة المعقدة، وهنا يأتي دور نقاط الوقوف breakpoints لحل تلك المشكلة.
تسمح نقاط الوقوف بتخطي التنفيذ إلى السطر الذي نريده مباشرةً وإيقاف البرنامج لمعاينة حالته آنذاك، حيث لإضافة نقطة وقوف في نود نضيف الكلمة المحجوزة debugger
ضمن الشيفرة مباشرةً، ويمكننا بعدها وخلال عملية التنقيح التنقل بين نقاط الوقوف ضمن الشيفرة بتنفيذ الأمر c
في طرفية التنقيح بدلًا من الأمر n
السابق، ويمكننا إضافة المراقبة للتعليمات التي نرغب بها عند نقاط الوقوف تلك.
سنتعرف على طريقة استخدام نقاط الوقوف بمثال عن برنامج يقرأ قائمة من الجمل ويستخرج منها الكلمة الأكثر تكرارًا ويعيدها لنا، لذلك سنُنشئ لهذا المثال ثلاث ملفات، الأول هو ملف يحوي الجمل النصية sentences.txt التي سيعالجها البرنامج، حيث سنضيف داخله كمثال أول فقرة من مقال عن سمكة قرش الحوت من موسوعة بريتانيكا Britannica بعد إزالة علامات الترقيم منها، لذلك ننشئ الملف ونفتحه ضمن محرر النصوص:
$ nano sentences.txt
ونكتب داخله النص التالي:
Whale shark Rhincodon typus gigantic but harmless shark family Rhincodontidae that is the largest living fish Whale sharks are found in marine environments worldwide but mainly in tropical oceans They make up the only species of the genus Rhincodon and are classified within the order Orectolobiformes a group containing the carpet sharks The whale shark is enormous and reportedly capable of reaching a maximum length of about 18 metres 59 feet Most specimens that have been studied however weighed about 15 tons about 14 metric tons and averaged about 12 metres 39 feet in length The body coloration is distinctive Light vertical and horizontal stripes form a checkerboard pattern on a dark background and light spots mark the fins and dark areas of the body
نحفظ الملف ونخرج منه، ونضيف الشيفرة التالية إلى ملف جافاسكربت جديد بالاسم textHelper.js، حيث سيحوي هذا الملف على بعض الدوال المساعدة في معالجة الملف النصي السابق خلال عملية تحديد الكلمة الأكثر تكرارًا من النص، ونبدأ بإنشاء الملف textHelper.js ونفتحه ضمن محرر النصوص:
$ nano textHelper.js
ونضيف ثلاث دوال لمعالجة النص ضمن الملف sentences.txt الأول لقراءة الملف:
const fs = require('fs'); const readFile = () => { let data = fs.readFileSync('sentences.txt'); let sentences = data.toString(); return sentences; };
نستورد الوحدة البرمجية fs
من نود لنتمكن من قراءة الملف، بعدها نضيف الدالة readFile()
التي تستخدم التابع readFileSync()
لتحميل محتوى الملف sentences.txt ككائن مخزن مؤقت Buffer
ثم تستدعي منه التابع toString()
لتحويل المحتوى إلى سلسلة نصية.
نضيف بعدها دالة لتجزئة السلسلة نصية السابقة إلى مصفوفة من الكلمات كالتالي:
... const getWords = (text) => { let allSentences = text.split('\n'); let flatSentence = allSentences.join(' '); let words = flatSentence.split(' '); words = words.map((word) => word.trim().toLowerCase()); return words; };
استفدنا من التوابع split()
و join()
و map()
لتحويل السلسلة النصية إلى مصفوفة من الكلمات الموجودة ضمنها، وحولنا حالة كل كلمة منها إلى أحرف صغيرة لتسهيل عملية المقارنة بينها وإحصائها.
أما الدالة الثالثة والأخيرة فستحصي تكرار كل كلمة ضمن مصفوفة الكلمات السابقة ويعيد كل الكلمات مع تكراراتها ضمن كائن يعبر عن النتيجة كالتالي:
... const countWords = (words) => { let map = {}; words.forEach((word) => { if (word in map) { map[word] = 1; } else { map[word] += 1; } }); return map; };
أنشأنا كائنًا جديدًا بالاسم map
يحوي الكلمات ضمن النص كمفاتيح وعدد مرات تكرارها كقيم لها، ثم مررنا على عناصر مصفوفة الكلمات وأضفناها إلى ذلك الكائن إن تكن موجودة أو زدنا قيمة تكرارها قيمة واحدة.
وأخيرًا لنصدر تلك الدوال لنتمكن من استخدامها ضمن الوحدات البرمجية الأخرى:
... module.exports = { readFile, getWords, countWords };
نحفظ الملف ونخرج منه، والآن سننشئ الملف الثالث والأخير ضمن المثال هو الملف الأساسي الذي سيستعين بالدوال ضمن الوحدة البرمجية السابقة textHelper.js لاستخراج أكثر كلمة تكرارًا من النص.
نبدأ بإنشاء الملف index.js ثم نفتحه ضمن محرر النصوص:
$ nano index.js
نستورد الوحدة البرمجية textHelpers.js كالتالي:
const textHelper = require('./textHelper');
وننشئ مصفوفة جديدة تحتوي على بعض الكلمات المكررة الشائعة التي نرغب بتجاهلها مثل حروف العطف والجر والضمائر وبعض الصفات، تدعى الكلمات الشائعة أو stop words:
... const stopwords = ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself', 'it', 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now', ''];
بهذه الطريقة سنحصل على كلمات ذات معاني من ضمن النص الذي نعالجه بدلًا من الحصول على كلمات مثل أدوات التعريف التي تتكرر كثيرًا مثل the و a.
نبدأ باستخدام الدوال المساعدة من الوحدة textHelper.js لقراءة النص واستخراج الكلمات منه وإحصاء مرات التكرار لكل منها كالتالي:
... let sentences = textHelper.readFile(); let words = textHelper.getWords(sentences); let wordCounts = textHelper.countWords(words);
بعد ذلك سنستخرج أكثر كلمة تكرارًا منها، وخوارزمية تحديد الكلمة الأكثر تكرارًا هي بالمرور أولًا على مفاتيح كائن الكلمات المحصاة ومقارنة التكرار مع آخر أعلى قيمة مررنا عليها سابقًا، وفي حال كانت قيمة التكرار للمفتاح الحالي أعلى من الكلمة السابقة سنحدد تكرار الكلمة الحالية على أنه التكرار الأعلى، لتصبح الشيفرة لهذه الخوارزمية كالتالي:
... let max = -Infinity; let mostPopular = ''; Object.entries(wordCounts).forEach(([word, count]) => { if (stopwords.indexOf(word) === -1) { if (count > max) { max = count; mostPopular = word; } } }); console.log(`The most popular word in the text is "${mostPopular}" with ${max} occurrences`);
استخدمنا التابع Object.entries()
لتحويل المفاتيح والقيم ضمن الكائن wordCounts
إلى مصفوفة، ثم استخدمنا التابع forEach()
وداخله عبارة شرطية لاختبار قيمة التكرار للكلمة الحالية مع أعلى قيمة تكرار شاهدناها سابقًا.
والآن نحفظ الملف ونخرج منه وننفذه كالتالي:
$ node index.js
نلاحظ ظهور النتيجة التالية:
The most popular word in the text is "whale" with 1 occurrences
لكن الجواب الذي ظهر خاطئ فنلاحظ تكرار الكلمة whale
أكثر من مرة ضمن النص في الملف sentences.txt، وهذه المرة قد يكون السبب في أحد الدوال العديدة المستخدمة في البرنامج، فقد تكون المشكلة في عملية قراءة محتوى الملف كاملًا، أو خلال معالجته وتحويله لمصفوفة الكلمات، أو خلال عملية توليد كائن إحصاء مرات التكرار للكلمات، أو قد يكون الخطأ في خوارزمية تحديد الكلمة الأكثر تكرارًا.
وأفضل أداة يمكن أن نستعين بها لتحديد الخطأ في مثل هذه الحالات هي أداة تنقيح الأخطاء، وحتى لو كانت شيفرة البرنامج الذي نعاينه قصيرة نسبيًا، فلا يفضل المرور سطرًا تلو الآخر خلال عملية التنفيذ وإضاعة الوقت، ويمكن بدلًا من ذلك الاستفادة من نقاط الوقوف للتوقف عند أماكن محددة مهمة لنا فقط، فمثلًا في نهاية جسم دالة لمعاينة القيمة التي ستعيدها.
لنبدأ بإضافة نقاط وقوف ضمن كل من التوابع المساعدة في الملف textHelper.js بإضافة الكلمة المحجوزة debugger
ضمن الشيفرة في تلك الأماكن، لذا نفتح الملف textHelper.js ضمن محرر النصوص ونضيف أول نقطة وقوف ضمن التابع readFile()
كالتالي:
... const readFile = () => { let data = fs.readFileSync('sentences.txt'); let sentences = data.toString(); debugger; return sentences; }; ...
بعدها نضيف نقطة وقوف أخرى ضمن الدالة getWords()
:
... const getWords = (text) => { let allSentences = text.split('\n'); let flatSentence = allSentences.join(' '); let words = flatSentence.split(' '); words = words.map((word) => word.trim().toLowerCase()); debugger; return words; }; ...
وأخيرًا نضيف نقطة وقوف للدالة countWords()
كالتالي:
... const countWords = (words) => { let map = {}; words.forEach((word) => { if (word in map) { map[word] = 1; } else { map[word] += 1; } }); debugger; return map; }; ...
نحفظ الملف ونخرج منه، ونبدأ جلسة تنقيح الأخطاء ومع أن كل نقاط الوقوف التي أضفناها موجودة ضمن الملف textHelpers.js لكن عملية تنقيح الأخطاء ستبدأ من الملف الرئيسي للتطبيق index.js، لذا ندخل لجلسة تنقيح الأخطاء من ذلك الملف كما تعلمنا سابقًا كالتالي:
$ node inspect index.js
ليظهر لنا التالي:
< Debugger listening on ws://127.0.0.1:9229/b2d3ce0e-3a64-4836-bdbf-84b6083d6d30 < For help, see: https://nodejs.org/en/docs/inspector < Debugger attached. Break on start in index.js:1 > 1 const textHelper = require('./textHelper'); 2 3 const stopwords = ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself', 'it', 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now', ''];
هذه المرة سننفذ الأمر c
وهو اختصار للكلمة continue وتعني إكمال التنفيذ لينتقل بذلك المنقح مباشرة إلى أول نقطة وقوف يصل إليها تنفيذ الشيفرة، وبعد الضغط على زر الإدخال ENTER لتنفيذ الأمر يظهر التالي:
break in textHelper.js:6 4 let data = fs.readFileSync('sentences.txt'); 5 let sentences = data.toString(); > 6 debugger; 7 return sentences; 8 };
نلاحظ كم من الوقت قد وفرنا في هذه العملية حيث توجهنا مباشرة إلى أول نقطة وقوف، ولنتأكد من أن هذه الدالة تعمل بشكل سليم وتقرأ محتوى الملف النصي كاملًا وتعيده، سنراقب المتغير sentences
لنعاين قيمته ونتأكد من صحة القيمة التي تعيدها الدالة:
debug> watch('sentences')
نتقدم بالتنفيذ خطوة للأمام فقط بتنفيذ الأمر n
لنعاين قيمة المتغير sentences
:
break in textHelper.js:7 Watchers: 0: sentences = 'Whale shark Rhincodon typus gigantic but harmless shark family Rhincodontidae that is the largest living fish\n' + 'Whale sharks are found in marine environments worldwide but mainly in tropical oceans\n' + 'They make up the only species of the genus Rhincodon and are classified within the order Orectolobiformes a group containing the carpet sharks\n' + 'The whale shark is enormous and reportedly capable of reaching a maximum length of about 18 metres 59 feet\n' + 'Most specimens that have been studied however weighed about 15 tons about 14 metric tons and averaged about 12 metres 39 feet in length\n' + 'The body coloration is distinctive\n' + 'Light vertical and horizontal stripes form a checkerboard pattern on a dark background and light spots mark the fins and dark areas of the body\n' 5 let sentences = data.toString(); 6 debugger; > 7 return sentences; 8 }; 9
تبدو القيمة صحيحة ولا مشاكل في عملية قراءة محتوى الملف إذًا فالمشكلة في مكان آخر.
لننتقل إلى نقطة الوقوف التالية بتنفيذ الأمر c
مجددًا ليظهر ما يلي:
break in textHelper.js:15 Watchers: 0: sentences = ReferenceError: sentences is not defined at eval (eval at getWords (your_file_path/debugger/textHelper.js:15:3), <anonymous>:1:1) at Object.getWords (your_file_path/debugger/textHelper.js:15:3) at Object.<anonymous> (your_file_path/debugger/index.js:7:24) at Module._compile (internal/modules/cjs/loader.js:1125:14) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10) at Module.load (internal/modules/cjs/loader.js:983:32) at Function.Module._load (internal/modules/cjs/loader.js:891:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12) at internal/main/run_main_module.js:17:47 13 let words = flatSentence.split(' '); 14 words = words.map((word) => word.trim().toLowerCase()); >15 debugger; 16 return words; 17 };
رسالة الخطأ التي ظهرت سببها مراقبتنا سابقًا لقيمة المتغير sentences
الذي لم يعد موجودًا الآن ضمن نطاق تنفيذ الدالة الحالية، حيث تبقى عملية المراقبة للمتغير طول مدة جلسة تنقيح الأخطاء، لذا سيتكرر ظهور رسالة الخطأ تلك ما دام المتغير لا يمكن الوصول إليه من مكان التنفيذ الحالي.
ويمكننا حل تلك المشكلة بإيقاف مراقبة المتغير باستخدام الدالة unwatch()
لإيقاف مراقبة المتغير sentences
بتنفيذ التعليمة التالية:
debug> unwatch('sentences')
لن تظهر أي رسالة عند تنفيذ التعليمة السابقة، والآن لنعود إلى الدالة getWords()
ونتأكد من صحة القيمة التي تعيدها وهي قائمة من كلمات النص السابق، لهذا نضيف مراقبة للمتغير words
كالتالي:
debug> watch('words')
وننتقل لتنفيذ السطر التالي بتنفيذ التعليمة n
ونعاين قيمة المتغير words
، ونلاحظ ظهور ما يلي:
break in textHelper.js:16 Watchers: 0: words = [ 'whale', 'shark', 'rhincodon', 'typus', 'gigantic', 'but', 'harmless', ... 'metres', '39', 'feet', 'in', 'length', '', 'the', 'body', 'coloration', ... ] 14 words = words.map((word) => word.trim().toLowerCase()); 15 debugger; >16 return words; 17 }; 18
لم يُظهر منقح الأخطاء محتوى المصفوفة كاملةً بسبب طولها وصعوبة قراءتها كاملة، ولكن ما ظهر يكفي ليؤكد أن محتوى النص ضمن المتغير sentences
تم تجزئته إلى كلمات بحالة أحرف صغيرة، أي أن الدالة getWords()
تعمل بشكل سليم.
والآن ننتقل لمعاينة الدالة الثالثة وهي countWords()
، ولكن أولًا سنزيل المراقبة للمصفوفة words
كي لا يظهر لنا رسالة خطأ كما حدث سابقًا عند الانتقال إلى نقطة الوقوف التالية كالتالي:
debug> unwatch('words')
ثم ننفذ الأمر c
لينتقل التنفيذ إلى نقطة الوقوف التالية ويظهر ما يلي:
break in textHelper.js:29 27 }); 28 >29 debugger; 30 return map; 31 };
سنتأكد ضمن هذه الدالة من احتواء المتغير map
على كل الكلمات السابقة مع قيم تكرارها، لذا نبدأ مراقبة المتغير map
كالتالي:
debug> watch('map')
ثم ننتقل بالتنفيذ إلى السطر التالي بتنفيذ الأمر n
ليظهر لنا ما يلي:
break in textHelper.js:30 Watchers: 0: map = { 12: NaN, 14: NaN, 15: NaN, 18: NaN, 39: NaN, 59: NaN, whale: 1, shark: 1, rhincodon: 1, typus: NaN, gigantic: NaN, ... } 28 29 debugger; >30 return map; 31 }; 32
على ما يبدو أن هذه الدالة هي سبب المشكلة وعملية إحصاء تكرار الكلمات خاطئة، ولمعرفة سبب الخطأ يجب أن نعاين عمل هذه الدالة ضمن حلقة المرور على عناصر المصفوفة words
، لذا سنعدل أماكن نقاط الوقوف الحالية.
نبدأ بالخروج من منقح الأخطاء بتنفيذ الأمر التالي:
debug> .exit
ثم نفتح الملف textHelper.js ضمن محرر النصوص لنعدل نقاط الوقوف ضمنه:
$ nano textHelper.js
بما أننا تأكدنا من صحة عمل الدالتين readFile()
و getWords()
سنزيل نقاط الوقوف من داخلهما، ونزيل نقطة الوقوف من نهاية الدالة countWords()
ونضيف نقطتي وقوف جديدتين في بداية ونهاية الدالة forEach()
، ليصبح الملف textHelper.js كالتالي:
... const readFile = () => { let data = fs.readFileSync('sentences.txt'); let sentences = data.toString(); return sentences; }; const getWords = (text) => { let allSentences = text.split('\n'); let flatSentence = allSentences.join(' '); let words = flatSentence.split(' '); words = words.map((word) => word.trim().toLowerCase()); return words; }; const countWords = (words) => { let map = {}; words.forEach((word) => { debugger; if (word in map) { map[word] = 1; } else { map[word] += 1; } debugger; }); return map; }; ...
نحفظ الملف ونخرج منه ثم نبدأ جلسة تنقيح أخطاء جديدة كالتالي:
$ node inspect index.js
كي نحدد سبب المشكلة يجب أن نراقب عدة قيم، أولها قيمة الكلمة الحالية word
المُمررة كمعامل من قبل تابع حلقة التكرار forEach()
كالتالي:
debug> watch('word')
لا تقتصر ميزة المراقبة ضمن جلسة تنقيح الأخطاء على المتغيرات فحسب، بل يمكن مراقبة قيم تعابير جافاسكربت البرمجية المستخدمة ضمن الشيفرة، كأن نراقب قيمة تنفيذ التعليمة الشرطية word in map
والتي تحدد ما إذا كانت الكلمة الحالية موجودة مسبقًا، ويمكن مراقبتها بتنفيذ التالي:
debug> watch('word in map')
لنضيف مراقبة لقيمة تكرار الكلمة الحالية ضمن متغير النتيجة map
كالتالي:
debug> watch('map[word]')
لا تقتصر ميزة المراقبة على التعابير البرمجية الموجودة ضمن الشيفرة فحسب، بل يمكن إضافة أي تعابير برمجية نريدها ليتم تنفيذها ومراقبة قيمتها، لذا سنستفيد من هذه الميزة ونضيف مراقبة لقيمة طول الكلمة الحالية ضمن المتغير word
:
debug> watch('word.length')
بعد أن انتهينا من إضافة القيم التي نريد مراقبتها أثناء التنفيذ سننفذ الأمر c
ونراقب كيف تعالج الدالة أول كلمة من مصفوفة الكلمات ضمن الحلقة داخل الدالة countWords()
، ليظهر لنا ما يلي:
break in textHelper.js:20 Watchers: 0: word = 'whale' 1: word in map = false 2: map[word] = undefined 3: word.length = 5 18 let map = {}; 19 words.forEach((word) => { >20 debugger; 21 if (word in map) { 22 map[word] = 1;
الكلمة الأولى التي يتم معالجتها هي whale
ولا يحوي الكائن map
على مفتاح للكلمة whale
لأنه فارغ، لذا قيمة المراقبة للكلمة الحالية whale
ضمن الكائن map
كما نلاحظ هي undefined
، وطول الكلمة الحالية whale
هو 5
، وهذه القيمة تحديدًا لا تفيدنا في البحث عن سبب الخطأ، ولكننا أضفناها لنتعلم كيف يمكن حساب ومراقبة أي تعبير برمجي خلال جلسة تنقيح الأخطاء.
والآن ننفذ التعليمة c
لنرى ماذا سيحدث في نهاية تنفيذ الدورة الحالية ليظهر لنا ما يلي:
break in textHelper.js:26 Watchers: 0: word = 'whale' 1: word in map = true 2: map[word] = NaN 3: word.length = 5 24 map[word] += 1; 25 } >26 debugger; 27 }); 28
أصبحت قيمة العبارة word in map
صحيحة true
بسبب إضافة مفتاح للكلمة الحالية whale
ضمن الكائن map
، ولكن قيمة المفتاح whale
ضمن الكائن map
هي NaN
ما يدل على وجود مشكلة ما، وتحديدًا في العبارة الشرطية if
ضمن الدالة countWords()
، فوظيفتها هي تحديد فيما إذا كنا سنضيف مفتاحًا جديدًا للكلمة الحالية إذا لم تكن موجودة سابقًا، أو إضافة واحد لقيمة المفتاح إن كان موجودًا مسبقًا، والصحيح هو تعيين القيمة map[word]
إلى 1
إذا لم تكن الكلمة word
موجودة كمفتاح ضمن map
، بينما حاليًا نحن نضيف قيمة واحد في حال العثور على word
وهو عكس المطلوب.
وكما لاحظنا في بداية الحلقة كانت قيمة التكرار للكلمة الحالية map["whale"]
غير موجودة undefined
، وفي جافاسكربت إذا حاولنا إضافة واحد إلى تلك القيمة undefined + 1
سينتج عن تلك العملية القيمة NaN
وهو ما ظهر بالفعل، ولتصحيح هذه المشكلة يمكننا تعديل الشرط ضمن if
، فبدلًا من أن يكون word in map
ننفي هذه العبارة لتصبح كالتالي !(word in map)
، حيث يُستخدم الرمز !
لنفي العبارات المنطقية فيصبح الشرط صحيحًا إذا لم يحتوي الكائن map
على مفتاح للقيمة word
.
والآن لننفذ هذا التعديل ضمن الدالة countWords()
ونختبرها مجددًا، لكن نخرج أولًا من جلسة تنقيح الأخطاء كالتالي:
debug> .exit
ونفتح الملف textHelper.js مجددًا ضمن محرر النصوص:
$ nano textHelper.js
نعدل الدالة countWords()
بالشكل التالي:
... const countWords = (words) => { let map = {}; words.forEach((word) => { if (!(word in map)) { map[word] = 1; } else { map[word] += 1; } }); return map; }; ...
نحفظ الملف ونخرج منه، وننفذ البرنامج ونراقب النتيجة:
$ node index.js
تظهر لنا النتيجة التالية هذه المرة:
The most popular word in the text is "whale" with 3 occurrences
وهي إجابة منطقية وأفضل من السابقة، ونلاحظ كيف ساعدنا منقح الأخطاء في تحديد الدالة التي كانت سبب المشكلة وتمييز الدوال التي تعمل بشكل سليم وساعدنا في اكتشاف سبب الخطأ، وبذلك نكون قد تعلمنا طريقة استخدام منقح الأخطاء الخاص بنود من سطر الأوامر.
وتعلمنا أيضًا كيف يمكن إضافة نقاط الوقوف باستخدام الكلمة debugger
وإعداد مراقبة لمختلف القيم والعبارات البرمجية لمراقبة حالة البرنامج أثناء التنفيذ وكل ذلك من سطر الأوامر، ولكن لتوفير تجربة استخدام أسهل يمكن إجراء العملية نفسها عبر واجهة مستخدم مرئية، وهذا ما سنتعرف عليه في الفقرة التالية.
سنتعلم في الفقرة التالية طريقة استخدام منقح الأخطاء من أدوات المطور في متصفح جوجل كروم، حيث سنبدأ جلسة لتنقيح الأخطاء في نود كما فعلنا سابقًا، وسنستعمل صفحة مخصصة من واجهة متصفح كروم لتعيين نقاط الوقوف وعمليات المراقبة من واجهة مرئية بدلًا من سطر الأوامر.
تنقيح الأخطاء في نود باستخدام أدوات المطور في كروم
تعد أدوات المطور في متصفح كروم من أشهر أدوات نتقيح الأخطاء لشيفرة جافاسكربت عمومًا ونود خصوصًا ضمن متصفح الويب، وذلك لأن محرك جافاسكربت المستخدم من قبل نود V8 هو نفسه المستخدم في متصفح كروم، لذا فالتكامل بينهما يوفر تجربة مرنة لتنقيح الأخطاء.
سنطبق في هذه الفقرة على مثال بسيط وهو خادم HTTP في نود مهمته إعادة قيمة بصيغة JSON كرد على الطلبات الواردة، وسنستخدم لاحقًا منقح الأخطاء لإعداد نقاط الوقوف ومراقبة عمل ذلك الخادم وتحديدًا كيف يتم توليد قيمة الرد على الطلبات الواردة، وللمزيد حول عملية إنشاء الخادم، راجع مقالة إنشاء خادم ويب في Node.js باستخدام الوحدة HTTP.
نبدأ بإنشاء ملف جافاسكربت جديد بالاسم server.js سيحوي على برنامج الخادم ونفتح الملف ضمن محرر النصوص كالتالي:
$ nano server.js
مهمة الخادم هي إعادة العبارة Hello World
بصيغة JSON ضمن الرد، حيث سيحوي على مصفوفة لعدة ترجمات لتلك العبارة ليختار إحداها عشوائيًا ويعيدها ضمن جسم الرد بصيغة JSON، وسيستمع الخادم إلى الطلبات الواردة على العنوان المحلي localhost
وعلى المنفذ رقم :8000
.
والآن نبدأ بإضافة شيفرة البرنامج كما يلي:
const http = require("http"); const host = 'localhost'; const port = 8000; const greetings = ["Hello world", "Hola mundo", "Bonjour le monde", "Hallo Welt", "Salve mundi"]; const getGreeting = function () { let greeting = greetings[Math.floor(Math.random() * greetings.length)]; return greeting }
استوردنا الوحدة برمجية http
والتي تساعد في إعداد خادم HTTP، ثم وضعنا قيم عنوان الخادم ورقم المنفذ ضمن المتغيرين host
و port
لاستخدامها لاحقًا لتشغيل الخادم، ثم عرفنا مصفوفة العبارات greetings
والتي تحوي على جميع العبارات الممكن إرسالها من قبل الخادم لتختار الدالة getGreeting()
إحداها عشوائيًا ويعيده.
والآن سنضيف دالة معالجة طلبات HTTP القادمة للخادم وشيفرة بدء تشغيل الخادم كالتالي:
... const requestListener = function (req, res) { let message = getGreeting(); res.setHeader("Content-Type", "application/json"); res.writeHead(200); res.end(`{"message": "${message}"}`); }; const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
أصبح الخادم بذلك جاهزًا للخطوة التالية وهي إعداد منقح أخطاء كروم، لهذا نبدأ جلسة تنقيح الأخطاء بتنفيذ الأمر التالي:
$ node --inspect server.js
ملاحظة: نلاحظ الفرق بين أمر بدء منقح الأخطاء الخاص بنود من سطر الأوامر وبين أمر منقح الأخطاء الخاص بكروم، حيث ننفذ الأمر inspect
للأول، أما للثاني نمرر الخيار --inspect
.
وبعد تشغيل منقح الأخطاء سنلاحظ ظهور ما يلي:
Debugger listening on ws://127.0.0.1:9229/996cfbaf-78ca-4ebd-9fd5-893888efe8b3 For help, see: https://nodejs.org/en/docs/inspector Server is running on http://localhost:8000
يمكننا الآن فتح متصفح جوجل كروم أو كروميوم Chromium والذهاب للعنوان chrome://inspect
من شريط العنوان في الأعلى، ويمكن أيضًا استعمال منقح الأخطاء لمتصفح مايكروسوفت إيدج Microsoft Edge ولكن بالذهاب إلى العنوان edge://inspect
بدلًا من العنوان السابق.
وبعد الذهاب لذلك العنوان ستظهر لنا الصفحة التالية:
نذهب لقسم الأجهزة Devices ونضغط على أمر فتح أداوت المطور الخاصة بنود "Open dedicated DevTools for Node" لتظهر لنا نافذة منفصلة كالتالي:
يمكننا الآن تنقيح أخطاء برنامج نود السابق بواسطة كروم، لذلك نذهب إلى تبويب المصادر Sources ونوسع قسم شجرة الملفات الظاهر على اليسار ونختار منه ملف البرنامج الخاص بنا وهو server.js:
ونضيف نقطة وقوف ضمن الشيفرة التي تظهر، حيث نريد التوقف بعد أن يختار البرنامج عبارة الترحيب التي سيعيدها ضمن الرد لنعاينها، لذلك يمكننا الضغط مباشرة على رقم السطر 10 لتظهر نقطة حمراء بجانبه ما يدل على إضافة نقطة وقوف في هذا السطر، وهو ما نلاحظه من قائمة نقاط الوقوف في اللوحة على اليمين:
لنراقب الآن عبارة برمجية، حيث يمكننا ذلك من اللوحة على اليمين وتحديدًا بجانب عنوان قسم المراقبة Watch بالضغط على علامة الزائد "+"، ونضيف اسم المتغير greeting
لنراقب قيمته أثناء التنفيذ ثم نضغط على زر الإدخال ENTER.
والآن لنبدأ بتنقيح البرنامج، فنذهب ضمن نافذة المتصفح إلى عنوان الذي يستمع إليه الخادم http://localhost:8000
وبعد الضغط على زر الإدخال ENTER للذهاب إلى ذلك العنوان سنلاحظ عدم ظهور أي رد مباشرة بل ستظهر لنا نافذة تنقيح الأخطاء في الواجهة مجددًا، وفي حال لم تظهر النافذة يمكن الذهاب إليها يدويًا لنلاحظ ظهور ما يلي:
حيث توقف تنفيذ الخادم عند نقطة الوقوف التي عيّناها سابقًا، ونلاحظ تحديث قيم المتغيرات التي نراقبها في لوحة المراقبة على الجانب الأيمن، وكذلك تظهر تلك القيمة بجانب السطر الحالي ضمن الشيفرة.
ولمتابعة تنفيذ الشيفرة نضغط على زر المتابعة الموجود في اللوحة على الجانب الأيمن فوق العبارة "Paused on breakpoint" والتي تعني توقف التنفيذ عند نقطة الوقوف، وبعد اكتمال التنفيذ ستلاحظ ظهور رد بصيغة JSON ضمن نافذة المتصفح التي تواصلنا منها مع الخادم:
{"message": "Hello world"}
نلاحظ أننا لم نضيف أي عبارات ضمن الشيفرة أو نعدل عليها لإضافة نقاط الوقوف، وهي الفائدة التي تقدمها أدوات تنقيح الأخطاء من الواجهة المرئية مثل كروم، وهو الخيار الأفضل لمن لا يرغب بالتعامل مع سطر الأوامر ويفضل التعامل مع الواجهات المرئية.
ختامًا
تعلمنا في هذا المقال طريقة التعامل مع منقح الأخطاء في تطبيقات نود وطريقة إعداد الراصدات لمراقبة حالة التطبيق، وتعلمنا طريقة استخدام نقاط الوقوف لمعاينة تنفيذ البرنامج في عدة أماكن ضمن البرنامج أثناء عمله، وتعاملنا مع كل من منقح أخطاء نود من سطر الأوامر ومن متصفح جوجل كروم من أدوات المطور الخاصة به، وذلك بدلًا من إضافة تعليمات الطباعة للقيم المختلفة داخل البرنامج.
يمكن الاستعانة بمنقح الأخطاء ما يسهل عملية استكشاف أخطاء التنفيذ ضمن البرنامج ومعاينة حالته، ما يوفر من وقت التطوير وخصوصًا وقت حل المشكلات وإصلاح الأخطاء.
ويمكن الرجوع إلى توثيق نود الرسمي عن أدوات تنقيح الأخطاء أو دليل أدوات المطور من كروم ودليل أدوات المطور لتنقيح شيفرة جافاسكربت.
تُعَد الأخطاء البرمجية أمرًا شائعًا في المجال البرمجي، وللتعرف أكثر عليها وعلى كيفية التعامل معها عامةً ننصحك بالاطلاع على الفيديو الآتي:
ترجمة -وبتصرف- للمقال How To Debug Node.js with the Built-In Debugger and Chrome DevTools لصاحبه Stack Abuse.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.