โหมดมืด
บทที่ 11 — Error Handling: จัดการข้อผิดพลาด
โปรแกรมจริงต้องเจอ "ข้อผิดพลาด" เสมอ — server ล่ม, ผู้ใช้กรอกข้อมูลผิด, ไฟล์หาย. บทนี้สอนวิธีจัดการให้โปรแกรมไม่พังทั้งระบบ
อ่านจบบทนี้คุณจะ:
- เข้าใจ Error object และวิธีสร้าง error ของตัวเอง
- ใช้
try/catch/finallyได้ถูกต้อง - จัดการ error ในโค้ด async
- รู้ว่าควร "throw" หรือ "return error" เมื่อไหร่
ใช้เวลา 2 ชั่วโมง
Part 1: Error คืออะไร
1.1 2 ประเภทของปัญหา
ในโปรแกรมมีปัญหา 2 แบบ:
1. Bug (ข้อผิดพลาดของ programmer) — โค้ดเขียนผิด เช่นเรียก property ของ undefined, พิมพ์ชื่อตัวแปรผิด → ต้องแก้โค้ด
2. Error ที่คาดได้ (สถานการณ์ที่อาจเกิด) — server ล่ม, ผู้ใช้กรอกผิด, ไฟล์ไม่มี → ต้อง "จัดการ" ให้โปรแกรมรับมือได้
บทนี้เน้นเรื่องที่ 2 — การ "จัดการ" error ที่อาจเกิด
1.2 ถ้าไม่จัดการ error จะเกิดอะไร
ตัวอย่างด้านล่างใช้ JSON.parse() ซึ่งเป็นฟังก์ชันในตัวสำหรับแปลง string ให้เป็น object (จะเรียนละเอียดเรื่อง JSON ในบทต่อ ๆ ไป) — ถ้าข้อมูลที่ส่งเข้ามาไม่ใช่ JSON ที่ถูกต้อง มันจะ throw error
javascript
const data = JSON.parse("ข้อความที่ไม่ใช่ JSON");
console.log("บรรทัดนี้ไม่ทำงาน");JSON.parse เจอข้อความผิดรูปแบบ → มัน "throw error" → โปรแกรมหยุดทันที บรรทัดถัดไปไม่ทำงาน
ใน console จะเห็นข้อความประมาณนี้ (ข้อความจริงต่างเล็กน้อยตาม browser/Node):
text
SyntaxError: Unexpected token 'ข', "ข้อความที่ไม่ใช่ JSON" is not valid JSON
at JSON.parse (<anonymous>)
at ...แปลเป็นไทย: "ผิด syntax — เจอตัวอักษร ข ที่ไม่คาดคิด, ข้อความ ข้อความที่ไม่ใช่ JSON ไม่ใช่ JSON ที่ถูกต้อง" — บรรทัดที่ขึ้นต้นด้วย at คือ "ร่องรอย" (stack trace) ที่บอกว่า error เกิดจากการเรียก JSON.parse
→ ถ้าเป็นเว็บ — ทั้งหน้าอาจค้าง. เราต้อง "จับ" error นี้ไว้
Part 2: Error Object
2.1 Error คือ object
ใน JavaScript error ไม่ใช่แค่ข้อความ แต่เป็น object ที่มี property สำคัญ — message (ข้อความ), name (ชนิด), และ stack (บอกว่า error เกิดที่บรรทัดไหน) เข้าใจโครงสร้างนี้ก่อนจะช่วยให้จัดการ error ได้ดี:
javascript
const error = new Error("เกิดข้อผิดพลาด");
error.message // "เกิดข้อผิดพลาด" — ข้อความ
error.name // "Error" — ชนิด
error.stack // ข้อความบอกว่า error เกิดที่บรรทัดไหน (call stack)2.2 ชนิดของ Error ที่มีมาให้
JavaScript มี Error หลายชนิดในตัว:
javascript
TypeError // ใช้ค่าผิดชนิด — เช่น เรียก method ของ undefined
RangeError // ค่าเกินขอบเขต — เช่น สร้าง array ขนาดติดลบ
ReferenceError // ใช้ตัวแปรที่ไม่มี — เช่น พิมพ์ชื่อตัวแปรผิด
SyntaxError // syntax ผิด — เช่น JSON ผิดรูปแบบ, วงเล็บไม่ครบแปลความหมายคร่าว ๆ:
- TypeError = "ผิดชนิดข้อมูล" (Type = ชนิด)
- RangeError = "เกินช่วงที่อนุญาต" (Range = ช่วง/ขอบเขต)
- ReferenceError = "อ้างถึงสิ่งที่ไม่มี" (Reference = การอ้างถึง)
- SyntaxError = "ไวยากรณ์ผิด" (Syntax = ไวยากรณ์)
แต่ละชนิดบอกประเภทของปัญหา — มีประโยชน์ตอนแยกแยะว่าเกิดอะไรขึ้น
2.3 throw — โยน error
throw = "ส่งสัญญาณว่าเกิดข้อผิดพลาด" — มันหยุดการทำงานปัจจุบันทันที:
javascript
function divide(a, b) {
if (b === 0) {
throw new Error("หารด้วยศูนย์ไม่ได้");
}
return a / b;
}⚠️ throw แต่ Error object เสมอ — อย่า throw primitive (string, number, boolean, null, undefined):
javascript
throw "error"; // ❌ ไม่ดี — ไม่มี stack trace
throw 42; // ❌ ไม่ดี — ไม่มีข้อมูลเพิ่ม
throw new Error("error"); // ✅ ดี — มีข้อมูลครบ💡
new Error("...")= สร้าง Error object ใหม่ (คล้ายกับnew Date()ที่เรียนในบทที่ 08) — ตัว object นี้มี.message,.name,.stackที่ช่วย debug ได้มาก
ทำไม? — Error object มี .stack ที่บอกว่า error เกิดที่ไหน ช่วย debug มาก. string ไม่มี
💡 stack trace คือรายการแสดงว่า error เกิดที่บรรทัดไหน และถูกเรียกผ่านฟังก์ชันไหนมาบ้าง — เห็นได้ใน console เวลาโปรแกรมพัง (บรรทัดที่ขึ้นต้นด้วย
atในตัวอย่าง Part 1.2)
Part 3: try / catch / finally
try/catch คือกลไกหลักในการ "จับ" error
3.1 พื้นฐาน
javascript
try {
const data = JSON.parse("ข้อความผิดรูปแบบ");
console.log(data);
} catch (error) {
console.log("จับ error ได้:", error.message);
}
console.log("โปรแกรมทำงานต่อได้"); // ✅ บรรทัดนี้ทำงานอธิบาย:
try { ... }— "ลองทำโค้ดนี้"catch (error) { ... }— "ถ้าเกิด error ในส่วน try → กระโดดมาทำตรงนี้ พร้อม error object"
ผลคือ — แม้ JSON.parse พัง โปรแกรมไม่หยุด — มันกระโดดมา catch แล้วทำต่อ
3.2 finally — ทำเสมอ
javascript
try {
doSomething();
} catch (error) {
handleError(error);
} finally {
cleanup(); // ทำเสมอ — ไม่ว่า try สำเร็จหรือ catch ทำงาน
}finally ทำงานเสมอ — ไม่ว่าจะมี error หรือไม่ — ใช้ทำ "งานเก็บกวาด" เช่นปิดไฟล์, ปิด connection
3.3 catch แบบไม่ใช้ error
ตั้งแต่ ES2019 ถ้าใน catch ไม่ได้ใช้ error object เลย เราละ parameter ทิ้งได้ (เขียน catch { เฉย ๆ) — โค้ดสะอาดขึ้นเมื่อแค่อยากรู้ว่า "พังหรือไม่":
javascript
try {
doSomething();
} catch {
console.log("มีบางอย่างผิดพลาด");
}3.4 try/catch จับอะไรได้บ้าง
try/catch จับได้:
- error จาก
throw - error ที่ JavaScript โยนเอง (เช่น TypeError)
try/catch จับไม่ได้:
- error ใน callback ที่ async (เช่นใน
setTimeout)
javascript
try {
setTimeout(() => {
throw new Error("พัง"); // ❌ try/catch จับไม่ได้!
}, 1000);
} catch (error) {
// ไม่มาถึงตรงนี้
}ทำไม? — เพราะ callback ของ setTimeout ทำงาน "ทีหลัง" (ตอนนั้น try/catch จบไปแล้ว) → เรื่องนี้ Part 5 อธิบายการจัดการ async error
⚠️ และเรื่องร้ายแรงกว่านั้น: ถ้า throw error ใน callback ของ
setTimeoutแล้วไม่มีใครจับ — ใน Node.js จะเป็นuncaughtExceptionทำให้ process crash หยุดทั้งโปรแกรม (default behavior). ในเบราว์เซอร์จะเห็น error ขึ้นใน console และ tab ทำงานต่อได้ แต่ใน Node ห้ามปล่อยทิ้งเด็ดขาด — ต้องห่อtry/catchใน callback เอง หรือใช้ Promise
Part 4: สร้าง Custom Error
ในงานจริง — เราสร้าง Error ชนิดของตัวเองเพื่อแยกแยะปัญหา
4.1 สร้าง class ที่ extends Error
เราสร้าง error ชนิดของตัวเองได้โดย extends Error — เพื่อแยกประเภท error (เช่น ValidationError, NotFoundError) และแนบข้อมูลเพิ่ม (เช่น field ที่ผิด) ทำให้ catch แล้วจัดการต่างกันตามชนิดได้:
💡 ทบทวนสั้น ๆ (จากบทที่ 06):
extends Error= "สร้าง error ชนิดใหม่ที่สืบทอดความสามารถจาก Error เดิม" —super(message)= บอกให้ Error ต้นแบบเก็บ message ให้ ถ้าลืมเรื่อง class/extends ลองกลับไปดูบทที่ 06 ก่อนได้
javascript
class ValidationError extends Error {
constructor(message, field) {
super(message); // เรียก constructor ของ Error
this.name = "ValidationError"; // ตั้งชื่อชนิด
this.field = field; // ใส่ข้อมูลเพิ่ม
}
}
class NotFoundError extends Error {
constructor(resource) {
super(`ไม่พบ ${resource}`);
this.name = "NotFoundError";
}
}4.2 ใช้ custom error
javascript
function createUser(data) {
if (!data.email) {
throw new ValidationError("ต้องกรอก email", "email");
}
if (!data.email.includes("@")) {
throw new ValidationError("email ไม่ถูกต้อง", "email");
}
// ...
}
try {
createUser({ name: "Alice" });
} catch (error) {
if (error instanceof ValidationError) {
console.log(`ฟิลด์ ${error.field}: ${error.message}`);
} else {
console.log("error อื่น:", error.message);
}
}instanceof ใช้เช็คว่า error เป็นชนิดไหน → จัดการต่างกันได้
4.3 Error Cause — ห่อ error เดิม
ES2022 เพิ่ม cause — ใช้ "ห่อ" error เดิมไว้ในข้อความใหม่:
📌 เวอร์ชันที่รองรับ: ฟีเจอร์นี้ใช้ได้ใน Node.js 16.9+, Chrome 93+, Firefox 91+, Safari 15+ — ปี 2026 ถือว่าใช้ได้ทุกที่แล้ว แต่ถ้าต้องรองรับ Node เก่ากว่านั้น (เช่น project legacy) อาจต้องเก็บ error เดิมไว้ใน property อื่นเอง
javascript
const url = "https://api.example.com/users/1"; // url ถูกกำหนดมาก่อน
try {
await fetch(url);
} catch (originalError) {
throw new Error("โหลดข้อมูลผู้ใช้ล้มเหลว", { cause: originalError });
}ปลายทาง:
javascript
catch (error) {
console.log(error.message); // "โหลดข้อมูลผู้ใช้ล้มเหลว"
console.log(error.cause); // error เดิมจาก fetch
}→ ใช้บอก "ระดับสูง" ว่าเกิดอะไร พร้อมเก็บ "ระดับล่าง" (สาเหตุจริง) ไว้
📌
error.causeอาจเป็นค่าอะไรก็ได้ (ไม่จำเป็นต้องเป็น Error object) — เวลาใช้งานควรเช็คก่อน:error.cause instanceof Errorหรือใช้ optional chainingerror.cause?.messageเพื่อกันกรณีที่ cause เป็น string หรือ null
Part 5: จัดการ Error ใน Async
จากบทที่ 09 — โค้ด async มีวิธีจัดการ error ของตัวเอง
5.1 ใน Promise — ใช้ .catch
ในโค้ดแบบ Promise chain (.then) เราจับ error ด้วย .catch ต่อท้าย — มันดักทุก error ที่เกิดใน .then ก่อนหน้าทั้งหมดไว้ที่จุดเดียว:
javascript
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log("ผิดพลาด:", error.message));.catch จับ error จากทุก .then ก่อนหน้า
5.2 ใน async/await — ใช้ try/catch
javascript
async function loadUser() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.log("โหลดล้มเหลว:", error.message);
return null;
}
}ใน async/await — try/catch จับ error ของ Promise ที่ await ได้ (ต่างจากกรณี setTimeout ใน Part 3.4 — เพราะ await "รอ" ตรงนั้นจริง ๆ)
5.3 ⚠️ Unhandled Rejection — ลืมจับ error ของ Promise
javascript
async function risky() {
throw new Error("พัง");
}
risky(); // ❌ ไม่มีใครจับ error → "Unhandled Promise Rejection"ถ้าเรียก async function แล้วไม่จับ error — error จะ "หลุด" → ใน Node.js โปรแกรมอาจ crash
→ เรียก async function ต้องจับ error เสมอ:
javascript
risky().catch(error => console.log(error));
// หรือ
try { await risky(); } catch (error) { ... }5.4 ดักจับ error ที่หลุดทั้งหมด (safety net)
วางตาข่ายนิรภัยกัน error ที่หลุดจริง ๆ — โค้ดด้านล่างใช้ API พิเศษคนละตัวกัน: window มีเฉพาะในเบราว์เซอร์, process มีเฉพาะใน Node.js — ต้องเลือกใช้ตาม environment
💡 global คืออะไร —
windowและprocessเป็น "global object" คือตัวแปรที่เรียกใช้ได้ทันทีจากทุกที่ในโปรแกรม โดยไม่ต้องimportหรือประกาศเอง — เบราว์เซอร์เตรียมwindowไว้ให้, Node.js เตรียมprocessไว้ให้
💡 เลือกใช้อันไหน? — ถ้ารันโค้ดใน Node.js (เช่น ทำแบบฝึกหัดบนเครื่องส่วนตัว) ใช้
process.on— ถ้าเขียนโค้ดสำหรับเบราว์เซอร์ ใช้window.addEventListener— โดยปกติตอนเรียนเราใช้ Node ก็ให้ใช้process.on
javascript
// ในเบราว์เซอร์ — window มีให้อัตโนมัติ
window.addEventListener("unhandledrejection", (event) => {
console.log("Promise error ที่ไม่ถูกจับ:", event.reason);
});
// ใน Node.js — process เป็น global ที่ Node ให้มา
process.on("unhandledRejection", (reason) => {
console.log("Promise error ที่ไม่ถูกจับ:", reason);
});⚠️ Node 15+ — unhandled rejection ทำ process crash โดย default: ตั้งแต่ Node.js เวอร์ชัน 15 เป็นต้นมา ถ้ามี Promise rejection ที่ไม่ถูกจับ → process จะ exit ทันที (ก่อนหน้านี้แค่ขึ้น warning) — โค้ด legacy ที่ยังพึ่งพฤติกรรมเดิม (แค่ warning ไม่ crash) สามารถสั่งรันด้วย flag
--unhandled-rejections=warnได้ แต่นั่นเป็นทางแก้ชั่วคราวเท่านั้น — แนวทาง production ที่ถูกต้องคือจับ error ให้ครบตั้งแต่ต้น ไม่ใช่ใช้ flag เพื่อปิด safety check ถ้าใช้process.on('unhandledRejection', ...)ควร log error แล้วเรียกprocess.exit(1)เพื่อให้ process manager (เช่น PM2 — โปรแกรมช่วยดูแล/รีสตาร์ท Node.js process อัตโนมัติ เป็นเครื่องมือแยกต่างหาก ไม่ได้มากับ Node) รีสตาร์ทโปรแกรมให้ — ไม่ใช่กลืน error เงียบ ๆ
→ อันนี้เป็น "ตาข่ายสุดท้าย" — ไม่ใช่วิธีจัดการหลัก. ควรจับ error ตรงจุดที่เกิด
Part 6: throw หรือ return error — เลือกยังไง
มี 2 วิธีบอกว่า "เกิดข้อผิดพลาด" — และต้องเลือกให้ถูก
6.1 วิธี throw
วิธีแรกคือ throw — โยน error ออกไปให้ผู้เรียกจัดการด้วย try/catch เหมาะกับ error ที่ "ไม่ควรเกิด" และอยากหยุดการทำงานทันที:
javascript
function getUser(id) {
if (!id) throw new Error("ต้องระบุ id");
// ...
}6.2 วิธี return error (result pattern)
อีกวิธีคือ "คืน" ผลลัพธ์ที่บอกสถานะ (เช่น { ok: false, error: ... }) แทนการ throw — เหมาะกับ error ที่ "คาดว่าจะเกิดได้" อย่าง validation ทำให้ผู้เรียกต้องเช็คผลก่อนใช้ ชัดเจนกว่า:
javascript
function validateEmail(email) {
if (!email) return { ok: false, error: "ต้องกรอก email" };
if (!email.includes("@")) return { ok: false, error: "email ไม่ถูกต้อง" };
return { ok: true };
}
const result = validateEmail("test");
if (!result.ok) {
console.log(result.error);
}6.3 เลือกอันไหน
ใช้ throw เมื่อ — เป็นข้อผิดพลาดที่ "ไม่คาดคิด / ผิดปกติ":
- database เชื่อมไม่ได้
- argument ที่ส่งมาผิดอย่างร้ายแรง
- สถานการณ์ที่ "ไม่ควรเกิด"
ใช้ return error เมื่อ — เป็นกรณีที่ "คาดได้ เป็นเรื่องปกติ":
- form validation (ผู้ใช้กรอกผิดเป็นเรื่องปกติ)
- "ไม่พบข้อมูล" (อาจไม่มีจริง ๆ ก็ได้)
- login ผิด
หลักการ: ถ้ามันเป็น "เรื่องปกติที่เกิดได้บ่อย" → return error (เพราะ throw ทุกครั้งทำให้โค้ดวุ่นวาย). ถ้าเป็น "เรื่องผิดปกติร้ายแรง" → throw
Part 7: หลักการเขียน Error Handling ที่ดี
7.1 อย่ากลืน error เงียบ ๆ
javascript
// ❌ แย่มาก — error หายไปไม่มีใครรู้
try {
doSomething();
} catch (error) {
// ว่างเปล่า — กลืน error เงียบ ๆ
}ถ้าจับ error แล้วไม่ทำอะไรเลย — บั๊กจะซ่อนอยู่ หาไม่เจอ. อย่างน้อยต้อง log:
javascript
try {
doSomething();
} catch (error) {
console.error("เกิดข้อผิดพลาด:", error);
}7.2 จับ error ที่เฉพาะเจาะจง
อย่าจับ error แบบเหมารวมแล้วทำเหมือนกันหมด — ควรเช็คชนิด error (ด้วย instanceof) แล้วจัดการต่างกันตามประเภท ส่วน error ที่ไม่รู้จักควรโยนต่อ ไม่กลืนเงียบ:
javascript
// ❌ จับทุกอย่างแล้วทำเหมือนกันหมด
catch (error) {
console.log("เกิดข้อผิดพลาด");
}
// ✅ แยกแยะตามชนิด
catch (error) {
if (error instanceof ValidationError) {
showFormError(error.field);
} else if (error instanceof NotFoundError) {
show404Page();
} else {
throw error; // error ที่ไม่รู้จัก — โยนต่อ ให้คนข้างบนจัดการ
}
}7.3 ตรวจพบปัญหาแล้วหยุดเลย (fail fast) — เช็คก่อน ผิดก็หยุดทันที
fail fast = หลักการ "เจอปัญหาให้หยุดทันที ไม่ปล่อยให้ทำงานต่อ" — ดีกว่าปล่อยให้โปรแกรมทำงานต่อไปบนข้อมูลที่ผิด แล้วไปพังลึก ๆ ที่หาสาเหตุยาก (คำนี้แปลตรงตัวว่า "พังเร็ว" ซึ่งฟังดูไม่ดี แต่ในวงการ programming ถือเป็นแนวทางที่ดี เพราะพังให้เห็นทันทีดีกว่าพังแบบซ่อนอยู่)
javascript
function processOrder(order) {
if (!order) throw new Error("ไม่มี order");
if (!order.items) throw new Error("order ไม่มีรายการสินค้า");
if (order.items.length === 0) throw new Error("order ว่างเปล่า");
// ถึงตรงนี้ = order ถูกต้องแน่นอน
// ทำงานหลัก...
}เช็คเงื่อนไขผิด ๆ ก่อน แล้ว throw ทันที — ดีกว่าปล่อยให้ error ไปโผล่ลึก ๆ (จำ early return จากบทที่ 04)
7.4 อย่าใช้ exception (ข้อผิดพลาดที่โปรแกรมโยนออกมา) ควบคุมการทำงานปกติ
javascript
// ❌ ใช้ try/catch แทน if
try {
const user = users.find(u => u.id === id);
return user.name;
} catch {
return "ไม่พบ";
}
// ✅ ใช้ if เช็คตามปกติ
const user = users.find(u => u.id === id);
return user ? user.name : "ไม่พบ";try/catch มีไว้จับ "ข้อผิดพลาด" — ไม่ใช่แทน if ในการตัดสินใจปกติ
Part 8: Logging — บันทึกสิ่งที่เกิดขึ้น
console มี method หลายตัวสำหรับ log:
javascript
console.log("ข้อมูลทั่วไป");
console.info("ข้อมูล");
console.warn("คำเตือน"); // สีเหลือง
console.error("ข้อผิดพลาด"); // สีแดง
console.table([{a:1}, {a:2}]); // แสดงเป็นตาราง
console.time("งาน"); /* ... */ console.timeEnd("งาน"); // จับเวลาในงาน production จริง — เราใช้ logging library (เช่น pino, winston สำหรับ Node.js) ที่บันทึก log แบบมีโครงสร้าง ส่งไปเก็บที่ส่วนกลาง — แต่ตอนเรียน console ก็พอ
8.1 รายงาน error ใน production
ในเว็บจริง — เราใช้ service ที่รวบรวม error จากผู้ใช้ทุกคน สำหรับมือใหม่แนะนำ Sentry เพราะมี free tier และเอกสารดี (มีคู่แข่งเช่น Bugsnag, Rollbar ด้วย — แต่เริ่มด้วย Sentry ก็พอ):
Sentry = บริการคลาวด์ที่รับ error report จากแอปเรา รวมข้อมูล stack trace + เวอร์ชัน + browser + user — แล้วแสดงเป็น dashboard ให้ทีมเห็นว่า bug ไหนเจอบ่อย
javascript
// ตัวอย่างแนวคิด (ต้อง setup Sentry ก่อน)
try {
doSomething();
} catch (error) {
Sentry.captureException(error); // ส่ง error ไปเก็บที่ Sentry
}→ ทำให้รู้ว่าผู้ใช้จริงเจอ error อะไรบ้าง — ตอนนี้แค่รู้จักว่ามี
Part 9: Lab — ลงมือทำ
Lab 1: try/catch พื้นฐาน
ฝึกห่อโค้ดที่อาจพังด้วย try/catch — ตัวอย่างคลาสสิกคือ JSON.parse ที่โยน error เมื่อ input ไม่ใช่ JSON ที่ถูกต้อง แล้วคืน null แทนการปล่อยให้โปรแกรมล่ม:
javascript
function parseJSON(text) {
try {
return JSON.parse(text);
} catch (error) {
console.log("JSON ไม่ถูกต้อง:", error.message);
return null;
}
}
console.log(parseJSON('{"name":"Alice"}')); // { name: "Alice" }
console.log(parseJSON('ไม่ใช่ JSON')); // nullLab 2: Custom Error
ฝึกสร้าง custom error ที่แนบข้อมูลเพิ่ม (ยอดเงิน, จำนวนที่ขาด) แล้ว catch โดยเช็ค instanceof เพื่อจัดการเฉพาะกรณีนั้น — รวมทุกแนวคิดของบท:
javascript
class InsufficientFundsError extends Error {
constructor(balance, amount) {
super(`ยอดเงิน ${balance} ไม่พอจ่าย ${amount}`);
this.name = "InsufficientFundsError";
this.balance = balance;
this.amount = amount;
}
}
function withdraw(balance, amount) {
if (amount > balance) {
throw new InsufficientFundsError(balance, amount);
}
return balance - amount;
}
try {
withdraw(100, 500);
} catch (error) {
if (error instanceof InsufficientFundsError) {
console.log(`ขาดอีก ${error.amount - error.balance} บาท`);
}
}Lab 3: async error handling
ฝึกจัดการ error ในโค้ด async จริง — ใช้ try/catch ครอบ await fetch พร้อมเช็ค response.ok เพื่อแยกกรณี network error กับ HTTP error:
javascript
async function fetchUser(id) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.log("โหลด user ล้มเหลว:", error.message);
return null;
}
}
// บรรทัดด้านล่างใช้ top-level await — ต้องรันในไฟล์ ES module
// (ตั้ง "type": "module" ใน package.json หรือใช้นามสกุล .mjs)
// ถ้าใช้ CommonJS ให้ห่อใน async function: (async () => { ... })()
const user = await fetchUser(1);
console.log(user);Lab 4: Result pattern
javascript
function safeDivide(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
return { ok: false, error: "ต้องเป็นตัวเลข" };
}
if (b === 0) {
return { ok: false, error: "หารด้วยศูนย์ไม่ได้" };
}
return { ok: true, value: a / b };
}
const result = safeDivide(10, 2);
if (result.ok) {
console.log("ผลลัพธ์:", result.value);
} else {
console.log("ผิดพลาด:", result.error);
}Part 10: Checkpoint
- ถ้าไม่จับ error ที่ throw เกิดอะไรขึ้น?
- Error object มี property อะไรบ้าง?
- ทำไมต้อง
throw new Error(...)ไม่ใช่throw "string"? try/catch/finally—finallyทำงานเมื่อไหร่?try/catchจับ error ในsetTimeoutcallback ได้ไหม? ทำไม?- สร้าง custom error ทำยังไง?
- จัดการ error ใน async/await ใช้อะไร? ใน Promise chain ใช้อะไร?
- "Unhandled Promise Rejection" คืออะไร?
throwกับreturn error— เลือกอันไหนเมื่อไหร่?- ทำไมไม่ควร "กลืน error เงียบ ๆ"?
Part 11: สรุปบทนี้
- Error เป็น object — มี
.message,.name,.stack throw new Error(...)เสมอ — อย่า throw stringtry/catch/finally— catch จับ error, finally ทำเสมอtry/catchจับ error ใน async callback (setTimeout) ไม่ได้ — แต่จับ Promise ที่awaitได้- Custom Error (
extends Error) — แยกแยะชนิดของปัญหา - async error: ใช้
try/catchกับ async/await, ใช้.catchกับ Promise chain - เรียก async function ต้องจับ error เสมอ (ไม่งั้น unhandled rejection)
throwสำหรับเรื่องผิดปกติร้ายแรง / return error สำหรับเรื่องปกติ (validation)- อย่ากลืน error เงียบ — อย่างน้อยต้อง log
บทต่อไป — DOM: ควบคุมหน้าเว็บ