Skip to main content
0
ไม่มีหมวดหมู่

จัดการ Error ฉบับไม่ปวดสมอง

/

สวัสดีครับสำหรับบทความนี้ จะทำให้ชาวเดฟมือใหม่หลาย ๆ คนที่เป็นเดฟมือระเบิดอยากจะปาคีย์บอร์ดเพราะปวดหัวปวดตับกับ 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 เมื่อเกิดข้อผิดพลาด การบันทึกข้อผิดพลาดด้วยเครื่องมือที่เหมาะสม ไปจนถึงการจัดการกับโค้ดอะซิงโครนัสนั่นเองครับ

Sirasit Boonklang

Author Sirasit Boonklang

More posts by Sirasit Boonklang
Close Menu

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • คุกกี้ที่จำเป็น
    เปิดใช้งานตลอด

    ประเภทของคุกกี้มีความจำเป็นสำหรับการทำงานของเว็บไซต์ เพื่อให้คุณสามารถใช้ได้อย่างเป็นปกติ และเข้าชมเว็บไซต์ คุณไม่สามารถปิดการทำงานของคุกกี้นี้ในระบบเว็บไซต์ของเราได้
    รายละเอียดคุกกี้

  • คุกกี้สำหรับการติดตามทางการตลาด

    ประเภทของคุกกี้ที่มีความจำเป็นในการใช้งานเพื่อการวิเคราะห์ และ นำเสนอโปรโมชัน สินค้า รวมถึงหลักสูตรฟรี และ สิทธิพิเศษต่าง ๆ คุณสามารถเลือกปิดคุกกี้ประเภทนี้ได้โดยไม่ส่งผลต่อการทำงานหลัก เว้นแต่การนำเสนอโปรโมชันที่อาจไม่ตรงกับความต้องการ
    รายละเอียดคุกกี้

บันทึกการตั้งค่า