สวัสดีครับสำหรับบทความนี้ จะทำให้ชาวเดฟมือใหม่หลาย ๆ คนที่เป็นเดฟมือระเบิดอยากจะปาคีย์บอร์ดเพราะปวดหัวปวดตับกับ error ที่ทำให้เซิร์ฟดับทุกที ซึ่งหลายครั้งมาจากการจัดการกับ error ที่ไม่ดีในวันนี้เรามาดูกันดีกว่าครับว่าการจัดการ error ที่ดีต้องมีอะไรบ้าง?
เราต้องรู้จักการใช้ try-catch
try-catch เป็นวิธีการจัดการข้อผิดพลาดที่เกิดขึ้นในโค้ด เพื่อให้โปรแกรมดับหรือหยุดการทำงานไปเมื่อมี error เกิดขึ้น เราสามารถใช้มันจัดการกับ error แล้วให้โปรแกรมยังสามารถทำงานต่อไปได้เราสามารถใช้กับเคสต่าง ๆ ได้ เช่น ฟังก์ชันที่ไม่มีอยู่ หรือการคำนวณที่ผิดพลาด ตัวอย่างเช่น
จากนี้นี้เราจะเห็นว่า console.log(“Success”) จะไม่มีแม้แต่โอกาสที่จะได้ทำงาน เพราะโปรแกรมได้พังไปตั้งแต่ตอนที่เจอ error ในบรรทัดแรกแล้ว
แต่ถ้าเราใส่ try-catch โดยโค้ดไหนที่มันอาจจะเกิด error เราก็ใส่ไว้ใน try เพราะ try แปลว่า ลอง หมายถึงลองทำดู ถ้าพังหรือมีข้อผิดพลาดมันก็โยนเข้าไปใน catch แล้วเราก็จะสามารถดึงข้อความที่เอาข้อผิดพลาดมาแสดงได้จาก error object เช่น error.message
จะผลลัพธ์ของการรันเราจะเห็นได้ว่าการใช้ try…catch สามารถจัดการกับ error ที่เกิดขึ้น แล้วทำให้โปรแกรมยังทำงานต่อไปได้นั่นเอง
แต่ไม่ใช่ว่า “จับทั้งหมด” แต่ไม่รู้จักสักอย่าง
คือจากที่เรารู้ว่าสามารถใช้ catch ในการจับ error ได้ แต่เราไม่ได้เข้าใจว่า error นั้นคืออะไรและจะแก้ยังไง ไม่ได้คำนึงถึงเนื้อหาที่อยู่ใน error นั้น ๆ
try {
// เรียกฟังก์ชันที่อาจทำให้เกิดข้อผิดพลาด
noFunction(); // ฟังก์ชันนี้ไม่ได้ถูกประกาศ
} catch (error) {
// จับข้อผิดพลาดทั้งหมดแต่ไม่ได้ให้รายละเอียดที่มีประโยชน์
console.log("เกิดข้อผิดพลาดบางอย่าง");
}
console.log("Success");
JavaScriptการทำแบบนี้จะทำให้เกิดปัญหาเรารู้ว่ามี error แต่ไม่สามารถเอามาวิเคราะห์ต่อหรือหาวิธีแก้ไขได้เลย แล้วถ้าเราอยากปรับให้ดีขึ้น ต้องมีการแยกประเภทของ error เช่น ReferenceError, TypeError หรืออื่น ๆ เพื่อให้เราสามารถแยกและจัดการข้อผิดพลาดได้ ตัวอย่างโค้ดจะเป็นแบบนี้
try {
// เรียกฟังก์ชันที่อาจทำให้เกิดข้อผิดพลาด
noFunction(); // ฟังก์ชันนี้ไม่ได้ถูกประกาศ
} catch (error) {
// ตรวจสอบประเภทของข้อผิดพลาดและแสดงรายละเอียดที่ชัดเจน
if (error instanceof ReferenceError) {
console.error("ReferenceError: มีการอ้างอิงถึงฟังก์ชันหรือค่าที่ไม่ถูกต้อง:", error.message);
} else if (error instanceof TypeError) {
console.error("TypeError: การใช้งานข้อมูลชนิดที่ไม่ถูกต้อง:", error.message);
} else {
console.error("เกิดข้อผิดพลาดที่ไม่คาดคิด:", error.message);
}
// อาจเพิ่มการบันทึกข้อมูลสำหรับการดีบัก หรือวิธีแก้ไขเบื้องต้น
console.error("รายละเอียดเพิ่มเติม:", error.stack);
}
console.log("Success");
JavaScriptเอิ่มมม … เราต้องเขียนจัดการเองแบบนี้ทุกประเภทเลยหรอ คำตอบคือ ในเฟรมเวิร์กสมัยใหม่ ๆ อย่าง Express.js หรือ Nest.js รวมถึงไลบรารีอื่น ๆ ก็มีการรองรับไว้ให้ จะช่วยให้ทำงานง่ายขึ้น
การจัดการข้อผิดพลาดในรูปแบบที่ดีนั้นเป็นสิ่งที่เฟรมเวิร์กยอดนิยมอย่าง Express.js และ Nest.js รวมถึงไลบรารีอื่นๆ มีการรองรับไว้ให้ โดยมีการจัดการข้อผิดพลาดที่เป็นระบบมากขึ้น เพื่อช่วยให้ผู้พัฒนาสามารถทำงานได้ง่ายและจัดการข้อผิดพลาดได้อย่างมีประสิทธิภาพ
ใน Express.js เราสามารถจัดการ error ด้วยการใช้ middleware สำหรับการจัดการข้อผิดพลาดโดยจะมีการใช้ฟังก์ชันที่รับพารามิเตอร์สี่ตัว (err, req, res, next) เพื่อบอกว่าเป็น middleware สำหรับจัดการข้อผิดพลาดโดยเฉพาะ ตัวอย่างเช่นโค้ดนี้
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
JavaScriptฟังก์ชันนี้จะถูกเรียกใช้เมื่อมี error ขึ้นในแอปของเรา ไม่ว่าจะมาจากการทำงานของ route handler หรือ middleware อื่น ๆ
โดยปกติแล้ว middleware สำหรับการจัดการ error ส่วนใหญ่จะถูกเพิ่มท้ายสุดหลังจาก middleware อื่น ๆ เช่น bodyParser หรือ methodOverride เพื่อให้สามารถจัดการข้อผิดพลาดทั้งหมดที่เกิดขึ้นได้ในที่เดียว ตัวอย่างการใช้งานที่เป็นระบบมากขึ้น
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(methodOverride());
function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}
function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' });
} else {
next(err);
}
}
function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
JavaScriptกลับมาที่พื้นฐานควรมีการตั้งค่า Default เมื่อเกิด Error
การตั้งค่าเริ่มต้นหรือค่า default เมื่อเกิดข้อผิดพลาดเป็นอีกวิธีที่จะช่วยให้โปรแกรมของเราไม่พังเมื่อเจอ error ตัวอย่างเช่น ถ้าเรามีฟังก์ชันที่ต้องการค่าจากผู้ใช้ แต่ผู้ใช้ไม่ได้ใส่ค่ามา เราสามารถตั้งค่า default ให้กับตัวแปรนั้นได้
function greet(name) {
if (!name) {
name = "ผู้ใช้";
}
console.log(`สวัสดี, ${name}!`);
}
greet(); // Output: สวัสดี, ผู้ใช้!
JavaScriptหรือถ้าเรากำลังทำงานกับ API ที่อาจส่งค่าบางอย่างที่อาจจะทำให้ระบบเราบัค เราสามารถตั้งค่า default เพื่อป้องกันข้อผิดพลาดได้
try {
const response = await fetchData();
const data = response.data || [];
processData(data);
} catch (error) {
console.error("เกิดข้อผิดพลาดในการดึงข้อมูล:", error.message);
}
JavaScriptใช้เครื่องมือสำหรับการจัดการ Error Logging
หรือเราสามารถบันทึกข้อผิดพลาด (Error Logging) เพื่อใช้ในการ tracking และแก้ไขปัญหาที่เกิดขึ้นได้โดยเราสามารถใช้เครื่องมืออย่าง Winston, Morgan หรือ Sentry เข้าช่วยได้
ตัวอย่างการใช้ Winston สำหรับ Error Logging
const winston = require('winston');
const logger = winston.createLogger({
level: 'error',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log' }),
],
});
try {
// โค้ดที่อาจเกิดข้อผิดพลาด
riskyFunction();
} catch (error) {
logger.error({
message: error.message,
stack: error.stack,
});
}
JavaScriptโดยการใช้เครื่องมือเหล่านี้จะทำให้เรารู้ว่าเกิดข้อผิดพลาดอะไรขึ้นและที่ไหน ทำให้เข้าใจสาเหตุของข้อผิดพลาดเพื่อแก้ไขได้อย่างรวดเร็ว และสามารถนำข้อมูลที่ได้ไปปรับปรุงระบบให้มีความเสถียรมากขึ้นนั่นเอง!
อย่าลืมเรื่อง Async/Await และ Promise
ต่อมาเมื่อเราทำงานกับโค้ดที่เป็นแบบอะซิงโครนัส (Asynchronous) เช่นการเรียก API หรือการอ่านไฟล์ การจัดการข้อผิดพลาดจะซับซ้อนขึ้น เราก็จะต้องใช้ try-catch ร่วมกับ async/await หรือใช้เมธอด .catch() กับ Promise ด้วย
ตัวอย่างการใช้ Async/Await กับ Try-Catch
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error("ไม่สามารถดึงข้อมูลได้:", error.message);
return null; // หรือค่าที่คุณต้องการใช้เป็น default
}
}
JavaScriptตัวอย่างการใช้ Promise กับ .catch()
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// ทำงานกับข้อมูล
})
.catch(error => {
console.error("เกิดข้อผิดพลาด:", error.message);
});
JavaScriptทั้งหมดนี้ก็จะเป็นตัวอย่างการจัดการข้อผิดพลาดอย่างมีประสิทธิภาพ ไม่ว่าจะเป็นการใช้ try-catch อย่างถูกต้อง การตั้งค่า default เมื่อเกิดข้อผิดพลาด การบันทึกข้อผิดพลาดด้วยเครื่องมือที่เหมาะสม ไปจนถึงการจัดการกับโค้ดอะซิงโครนัสนั่นเองครับ