From 429667696550825563ee022afe4f84088e1e78d6 Mon Sep 17 00:00:00 2001 From: Prasit Tongpradit Date: Sun, 29 Mar 2026 16:50:05 +0700 Subject: [PATCH] docs: translate error-handling section (2 articles) --- .../1-finally-or-code-after/solution.md | 10 +- .../1-finally-or-code-after/task.md | 24 +- 1-js/10-error-handling/1-try-catch/article.md | 390 ++++++++---------- .../2-custom-errors/1-format-error/task.md | 10 +- .../2-custom-errors/article.md | 140 +++---- 1-js/10-error-handling/index.md | 2 +- 6 files changed, 275 insertions(+), 301 deletions(-) diff --git a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md index ec0dabc9a..4ca3eff05 100644 --- a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md +++ b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md @@ -1,8 +1,8 @@ -The difference becomes obvious when we look at the code inside a function. +ความแตกต่างจะเห็นชัดเมื่อดูโค้ดที่อยู่ภายในฟังก์ชัน -The behavior is different if there's a "jump out" of `try...catch`. +สองแบบนี้ทำงานต่างกันเมื่อมีการ "กระโดดออก" จาก `try...catch` -For instance, when there's a `return` inside `try...catch`. The `finally` clause works in case of *any* exit from `try...catch`, even via the `return` statement: right after `try...catch` is done, but before the calling code gets the control. +ยกตัวอย่างเช่น เมื่อมี `return` อยู่ใน `try...catch` บล็อก `finally` จะทำงานเสมอไม่ว่าจะออกจาก `try...catch` ด้วยวิธีไหน รวมถึง `return` ด้วย โดยจะรันหลังจาก `try...catch` ทำงานเสร็จ แต่ก่อนที่โค้ดที่เรียกฟังก์ชันจะได้รับค่ากลับ ```js run function f() { @@ -21,7 +21,7 @@ function f() { f(); // cleanup! ``` -...Or when there's a `throw`, like here: +...หรือเมื่อมี `throw` แบบนี้: ```js run function f() { @@ -44,4 +44,4 @@ function f() { f(); // cleanup! ``` -It's `finally` that guarantees the cleanup here. If we just put the code at the end of `f`, it wouldn't run in these situations. +`finally` นี่แหละที่รับประกันว่าจะเคลียร์งานได้เสมอ ถ้าเราแค่เขียนโค้ดไว้ต่อท้ายฟังก์ชัน `f` เฉยๆ โค้ดส่วนนั้นจะไม่ทำงานในสถานการณ์เหล่านี้ \ No newline at end of file diff --git a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md index b6dc81326..7b7766761 100644 --- a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md +++ b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md @@ -2,37 +2,37 @@ importance: 5 --- -# Finally or just the code? +# ใช้ `finally` หรือเขียนโค้ดต่อท้ายดี? -Compare the two code fragments. +ลองเปรียบเทียบโค้ดสองแบบนี้ -1. The first one uses `finally` to execute the code after `try...catch`: +1. แบบแรกใช้ `finally` รันโค้ดหลัง `try...catch`: ```js try { - work work + ทำงาน ทำงาน } catch (err) { - handle errors + จัดการ error } finally { *!* - cleanup the working space + เคลียร์งาน */!* } ``` -2. The second fragment puts the cleaning right after `try...catch`: +2. แบบที่สองเขียนโค้ดเคลียร์งานไว้ต่อท้าย `try...catch` เลย: ```js try { - work work + ทำงาน ทำงาน } catch (err) { - handle errors + จัดการ error } *!* - cleanup the working space + เคลียร์งาน */!* ``` -We definitely need the cleanup after the work, doesn't matter if there was an error or not. +เราต้องเคลียร์งานหลังทำงานเสร็จแน่นอน ไม่ว่าจะเกิด error หรือไม่ก็ตาม -Is there an advantage here in using `finally` or both code fragments are equal? If there is such an advantage, then give an example when it matters. +ถ้าอย่างนั้น ใช้ `finally` มีข้อได้เปรียบกว่าไหม หรือสองแบบนี้ให้ผลเหมือนกัน? ถ้ามีข้อได้เปรียบ ลองยกตัวอย่างกรณีที่มันสำคัญ \ No newline at end of file diff --git a/1-js/10-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md index cad2e1a3e..599369d8a 100644 --- a/1-js/10-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -1,118 +1,118 @@ -# Error handling, "try...catch" +# การจัดการ error ด้วย "try...catch" -No matter how great we are at programming, sometimes our scripts have errors. They may occur because of our mistakes, an unexpected user input, an erroneous server response, and for a thousand other reasons. +เขียนโค้ดเก่งแค่ไหน บางทีก็ยังมี error อยู่ดี — อาจเป็นเพราะเราเอง ผู้ใช้กรอกข้อมูลผิด เซิร์ฟเวอร์ตอบมาแปลกๆ หรืออีกร้อยแปดเหตุผล -Usually, a script "dies" (immediately stops) in case of an error, printing it to console. +ปกติพอเจอ error สคริปต์จะ "ตาย" ทันที แล้วพ่น error ออกมาที่คอนโซล -But there's a syntax construct `try...catch` that allows us to "catch" errors so the script can, instead of dying, do something more reasonable. +แต่ JavaScript มีท่าช่วยชีวิตอยู่ — `try...catch` ช่วยให้เรา "จับ" error ได้ แทนที่จะปล่อยให้สคริปต์ตายเฉยๆ ก็เอามาจัดการต่อได้เลย -## The "try...catch" syntax +## ไวยากรณ์ "try...catch" -The `try...catch` construct has two main blocks: `try`, and then `catch`: +เขียนง่ายๆ แค่ครอบโค้ดด้วย `try` กับ `catch`: ```js try { - // code... + // โค้ด... } catch (err) { - // error handling + // จัดการ error } ``` -It works like this: +ทำงานยังไงล่ะ? -1. First, the code in `try {...}` is executed. -2. If there were no errors, then `catch (err)` is ignored: the execution reaches the end of `try` and goes on, skipping `catch`. -3. If an error occurs, then the `try` execution is stopped, and control flows to the beginning of `catch (err)`. The `err` variable (we can use any name for it) will contain an error object with details about what happened. +1. รันโค้ดใน `try {...}` ก่อน +2. ถ้าไม่มี error — ข้าม `catch` ไปเลย รันต่อข้างล่างตามปกติ +3. แต่ถ้าเกิด error — `try` หยุดทันที แล้วกระโดดไปที่ `catch (err)` แทน ตัวแปร `err` (ตั้งชื่ออะไรก็ได้) จะเก็บรายละเอียดของ error ไว้ให้ ![](try-catch-flow.svg) -So, an error inside the `try {...}` block does not kill the script -- we have a chance to handle it in `catch`. +พูดง่ายๆ ก็คือ error ที่เกิดใน `try {...}` จะไม่ทำให้สคริปต์ตาย — เราจับมาจัดการได้ใน `catch` -Let's look at some examples. +ลองดูตัวอย่างกัน -- An errorless example: shows `alert` `(1)` and `(2)`: +- ตัวอย่างที่ไม่มี error: จะแสดง `alert` ที่ `(1)` กับ `(2)`: ```js run try { - alert('Start of try runs'); // *!*(1) <--*/!* + alert('เริ่มรัน try'); // *!*(1) <--*/!* - // ...no errors here + // ...ไม่มี error - alert('End of try runs'); // *!*(2) <--*/!* + alert('รัน try จบ'); // *!*(2) <--*/!* } catch (err) { - alert('Catch is ignored, because there are no errors'); // (3) + alert('ข้าม catch ไป เพราะไม่มี error'); // (3) } ``` -- An example with an error: shows `(1)` and `(3)`: +- ตัวอย่างที่มี error: จะแสดง `(1)` กับ `(3)`: ```js run try { - alert('Start of try runs'); // *!*(1) <--*/!* + alert('เริ่มรัน try'); // *!*(1) <--*/!* *!* - lalala; // error, variable is not defined! + lalala; // เกิด error เพราะตัวแปรยังไม่ได้ประกาศ! */!* - alert('End of try (never reached)'); // (2) + alert('รัน try จบ (ไม่มีทางถึงบรรทัดนี้)'); // (2) } catch (err) { - alert(`Error has occurred!`); // *!*(3) <--*/!* + alert(`เกิด error ขึ้น!`); // *!*(3) <--*/!* } ``` -````warn header="`try...catch` only works for runtime errors" -For `try...catch` to work, the code must be runnable. In other words, it should be valid JavaScript. +````warn header="`try...catch` ใช้ได้กับ runtime error เท่านั้นนะ" +`try...catch` จะทำงานได้ก็ต่อเมื่อโค้ดนั้นถูกไวยากรณ์ก่อน -It won't work if the code is syntactically wrong, for instance it has unmatched curly braces: +ถ้าโค้ดมีปัญหาเรื่องไวยากรณ์ เช่น ปีกกาไม่ครบคู่ จะจับ error ไม่ได้: ```js run try { {{{{{{{{{{{{ } catch (err) { - alert("The engine can't understand this code, it's invalid"); + alert("เอนจินอ่านโค้ดนี้ไม่รู้เรื่อง เพราะไวยากรณ์ผิด"); } ``` -The JavaScript engine first reads the code, and then runs it. The errors that occur on the reading phase are called "parse-time" errors and are unrecoverable (from inside that code). That's because the engine can't understand the code. +เพราะ JavaScript engine จะอ่านโค้ดทั้งหมดก่อนแล้วค่อยรัน ถ้าอ่านไม่ออกตั้งแต่แรก (เรียกว่า "parse-time" error) ก็จับไม่ได้เลย -So, `try...catch` can only handle errors that occur in valid code. Such errors are called "runtime errors" or, sometimes, "exceptions". +`try...catch` เลยจับได้แค่ error ที่เกิดตอนรันโค้ดที่ถูกไวยากรณ์แล้วเท่านั้น — พวกนี้เรียกว่า "runtime error" หรือ "exception" ```` -````warn header="`try...catch` works synchronously" -If an exception happens in "scheduled" code, like in `setTimeout`, then `try...catch` won't catch it: +````warn header="`try...catch` ทำงานแบบ synchronous นะ" +มีจุดสำคัญอีกอย่าง — ถ้า error เกิดในโค้ดที่ "ตั้งเวลาไว้" เช่น `setTimeout` ตัว `try...catch` จะจับไม่ได้: ```js run try { setTimeout(function() { - noSuchVariable; // script will die here + noSuchVariable; // สคริปต์จะตายตรงนี้ }, 1000); } catch (err) { - alert( "won't work" ); + alert( "จับไม่ได้" ); } ``` -That's because the function itself is executed later, when the engine has already left the `try...catch` construct. +ทำไมล่ะ? ก็เพราะฟังก์ชันข้างในจะรันทีหลัง ตอนนั้น engine ผ่านพ้น `try...catch` ไปแล้ว -To catch an exception inside a scheduled function, `try...catch` must be inside that function: +ทางแก้ก็คือใส่ `try...catch` ไว้ข้างในฟังก์ชันนั้นเลย: ```js run setTimeout(function() { - try { - noSuchVariable; // try...catch handles the error! + try { + noSuchVariable; // try...catch จับ error ได้! } catch { - alert( "error is caught here!" ); + alert( "จับ error ได้แล้ว!" ); } }, 1000); ``` @@ -120,92 +120,88 @@ setTimeout(function() { ## Error object -When an error occurs, JavaScript generates an object containing the details about it. The object is then passed as an argument to `catch`: +พอเกิด error JavaScript จะสร้างออบเจ็กต์ที่บรรจุรายละเอียดไว้ให้ แล้วโยนเข้ามาใน `catch`: ```js try { // ... -} catch (err) { // <-- the "error object", could use another word instead of err +} catch (err) { // <-- "error object" จะตั้งชื่อเป็นอะไรก็ได้ ไม่จำเป็นต้องเป็น err // ... } ``` -For all built-in errors, the error object has two main properties: +error built-in ทุกตัวจะมีพร็อพเพอร์ตี้หลักอยู่ 2 ตัว: `name` -: Error name. For instance, for an undefined variable that's `"ReferenceError"`. +: ชื่อของ error เช่น ถ้าใช้ตัวแปรที่ยังไม่ได้ประกาศ จะได้ `"ReferenceError"` `message` -: Textual message about error details. +: ข้อความอธิบายรายละเอียดของ error -There are other non-standard properties available in most environments. One of most widely used and supported is: +นอกจากนี้ยังมีอีกตัวที่ไม่ได้อยู่ในมาตรฐาน แต่เกือบทุกที่รองรับ: `stack` -: Current call stack: a string with information about the sequence of nested calls that led to the error. Used for debugging purposes. +: call stack ณ ขณะนั้น เป็นสตริงที่บอกลำดับการเรียกฟังก์ชันซ้อนกันจนนำไปสู่ error นั้น ใช้ประโยชน์ในการดีบัก -For instance: +ตัวอย่าง: ```js run untrusted try { *!* - lalala; // error, variable is not defined! + lalala; // เกิด error เพราะตัวแปรยังไม่ได้ประกาศ! */!* } catch (err) { alert(err.name); // ReferenceError alert(err.message); // lalala is not defined alert(err.stack); // ReferenceError: lalala is not defined at (...call stack) - // Can also show an error as a whole - // The error is converted to string as "name: message" + // แสดง error ทั้งก้อนก็ได้ + // ตัว error จะถูกแปลงเป็นสตริงในรูปแบบ "name: message" alert(err); // ReferenceError: lalala is not defined } ``` -## Optional "catch" binding +## ละ "catch" binding ก็ได้ (Optional "catch" binding) [recent browser=new] -If we don't need error details, `catch` may omit it: +ถ้าไม่สนรายละเอียดของ error ก็ละตัวแปรได้เลย: ```js try { // ... -} catch { // <-- without (err) +} catch { // <-- ไม่ใส่ (err) // ... } ``` -## Using "try...catch" - -Let's explore a real-life use case of `try...catch`. +## ลองใช้ "try...catch" กับงานจริง -As we already know, JavaScript supports the [JSON.parse(str)](mdn:js/JSON/parse) method to read JSON-encoded values. +มาดูเคสจริงๆ กันบ้าง -Usually it's used to decode data received over the network, from the server or another source. +JavaScript มีเมธอด [JSON.parse(str)](mdn:js/JSON/parse) ที่ใช้อ่านค่า JSON เราเจอมันบ่อยมากตอนรับข้อมูลจากเซิร์ฟเวอร์ -We receive it and call `JSON.parse` like this: +ปกติก็เรียกใช้แบบนี้: ```js run -let json = '{"name":"John", "age": 30}'; // data from the server +let json = '{"name":"John", "age": 30}'; // ข้อมูลจากเซิร์ฟเวอร์ *!* -let user = JSON.parse(json); // convert the text representation to JS object +let user = JSON.parse(json); // แปลงข้อความเป็นออบเจ็กต์ JS */!* -// now user is an object with properties from the string +// ตอนนี้ user เป็นออบเจ็กต์ที่มีพร็อพเพอร์ตี้ตามสตริงแล้ว alert( user.name ); // John alert( user.age ); // 30 ``` -You can find more detailed information about JSON in the chapter. +อ่านเพิ่มเรื่อง JSON ได้ที่ -**If `json` is malformed, `JSON.parse` generates an error, so the script "dies".** +**แต่ถ้า `json` มีรูปแบบผิดล่ะ? `JSON.parse` จะโยน error ออกมาแล้วสคริปต์ก็ตายเลย** -Should we be satisfied with that? Of course not! +จะปล่อยแบบนี้เหรอ? ไม่ได้สิ! ผู้ใช้จะไม่รู้อะไรเลยว่าเกิดอะไรขึ้น (ยกเว้นเปิดคอนโซลดู) ไม่มีใครชอบเวลาของหายไปเฉยๆ โดยไม่บอกอะไรสักคำ -This way, if something's wrong with the data, the visitor will never know that (unless they open the developer console). And people really don't like when something "just dies" without any error message. - -Let's use `try...catch` to handle the error: +เอา `try...catch` มาช่วยเลย: ```js run let json = "{ bad json }"; @@ -213,74 +209,68 @@ let json = "{ bad json }"; try { *!* - let user = JSON.parse(json); // <-- when an error occurs... + let user = JSON.parse(json); // <-- เมื่อเกิด error... */!* - alert( user.name ); // doesn't work + alert( user.name ); // ไม่ทำงาน } catch (err) { *!* - // ...the execution jumps here - alert( "Our apologies, the data has errors, we'll try to request it one more time." ); + // ...การทำงานจะกระโดดมาที่นี่ + alert( "ขออภัย ข้อมูลมีปัญหา เราจะลองโหลดข้อมูลใหม่อีกครั้ง" ); alert( err.name ); alert( err.message ); */!* } ``` -Here we use the `catch` block only to show the message, but we can do much more: send a new network request, suggest an alternative to the visitor, send information about the error to a logging facility, ... . All much better than just dying. +ตรงนี้เราแค่แสดงข้อความ แต่จริงๆ ทำได้อีกเยอะ — ส่ง request ใหม่ เสนอทางเลือกอื่นให้ผู้ใช้ หรือส่ง error ไปเก็บ log ก็ได้ ดีกว่าปล่อยให้สคริปต์ตายเฉยๆ เป็นไหนๆ -## Throwing our own errors +## โยน error เองก็ได้ -What if `json` is syntactically correct, but doesn't have a required `name` property? +แต่ถ้า `json` ไวยากรณ์ถูกหมด แต่ไม่มี `name` ที่เราต้องการล่ะ? -Like this: +แบบนี้: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ข้อมูลไม่ครบ try { - let user = JSON.parse(json); // <-- no errors + let user = JSON.parse(json); // <-- ไม่มี error *!* - alert( user.name ); // no name! + alert( user.name ); // ไม่มี name! */!* } catch (err) { - alert( "doesn't execute" ); + alert( "ไม่ทำงาน" ); } ``` -Here `JSON.parse` runs normally, but the absence of `name` is actually an error for us. - -To unify error handling, we'll use the `throw` operator. +`JSON.parse` ไม่ได้ฟ้อง error อะไร แต่สำหรับเราแล้ว ไม่มี `name` ก็ถือว่าข้อมูลไม่ครบ — ต้องเป็น error เหมือนกัน -### "Throw" operator +เราจะใช้ `throw` โยน error ออกมาเอง -The `throw` operator generates an error. +### ตัวดำเนินการ "throw" -The syntax is: +`throw` ใช้สร้าง error ขึ้นมาเอง เขียนแค่: ```js throw ``` -Technically, we can use anything as an error object. That may be even a primitive, like a number or a string, but it's better to use objects, preferably with `name` and `message` properties (to stay somewhat compatible with built-in errors). +จริงๆ จะโยนอะไรก็ได้ ตัวเลข สตริงก็ยังได้ แต่ควรใช้ออบเจ็กต์ที่มี `name` กับ `message` จะดีกว่า — จะได้สอดคล้องกับ error ที่ JavaScript สร้างเอง -JavaScript has many built-in constructors for standard errors: `Error`, `SyntaxError`, `ReferenceError`, `TypeError` and others. We can use them to create error objects as well. - -Their syntax is: +JavaScript เตรียมคอนสตรักเตอร์ error มาให้หลายตัว เช่น `Error`, `SyntaxError`, `ReferenceError`, `TypeError` เอาไปใช้สร้าง error ได้เลย: ```js let error = new Error(message); -// or +// หรือ let error = new SyntaxError(message); let error = new ReferenceError(message); // ... ``` -For built-in errors (not for any objects, just for errors), the `name` property is exactly the name of the constructor. And `message` is taken from the argument. - -For instance: +error built-in พวกนี้ `name` จะตรงกับชื่อคอนสตรักเตอร์เป๊ะ ส่วน `message` ก็มาจากอาร์กิวเมนต์ที่ส่งเข้าไป: ```js run let error = new Error("Things happen o_O"); @@ -289,7 +279,7 @@ alert(error.name); // Error alert(error.message); // Things happen o_O ``` -Let's see what kind of error `JSON.parse` generates: +ลองดูว่า `JSON.parse` โยน error ชนิดไหนออกมา: ```js run try { @@ -302,18 +292,16 @@ try { } ``` -As we can see, that's a `SyntaxError`. - -And in our case, the absence of `name` is an error, as users must have a `name`. +เห็นไหม — เป็น `SyntaxError` เลย -So let's throw it: +ในกรณีของเราไม่มี `name` ก็ต้องถือว่า error เหมือนกัน งั้นก็ throw เลย: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ข้อมูลไม่ครบ try { - let user = JSON.parse(json); // <-- no errors + let user = JSON.parse(json); // <-- ไม่มี error if (!user.name) { *!* @@ -328,44 +316,44 @@ try { } ``` -In the line `(*)`, the `throw` operator generates a `SyntaxError` with the given `message`, the same way as JavaScript would generate it itself. The execution of `try` immediately stops and the control flow jumps into `catch`. +บรรทัด `(*)` `throw` สร้าง `SyntaxError` พร้อม `message` ที่เรากำหนด — เหมือนกับที่ JavaScript สร้างเองเลย พอ throw ปุ๊บ `try` ก็หยุดทันทีแล้วกระโดดไป `catch` -Now `catch` became a single place for all error handling: both for `JSON.parse` and other cases. +ดูดีใช่ไหม? ตอนนี้ `catch` กลายเป็นจุดเดียวที่จัดการ error ทั้งหมด ไม่ว่าจะเป็น `JSON.parse` หรือ error ที่เราโยนเอง -## Rethrowing +## Rethrowing — โยนต่อ -In the example above we use `try...catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try {...}` block? Like a programming error (variable is not defined) or something else, not just this "incorrect data" thing. +แต่เดี๋ยวก่อน ถ้าใน `try {...}` เกิด *error อื่นที่เราไม่ได้คาดไว้* ล่ะ? เช่น พิมพ์ชื่อตัวแปรผิด หรือ bug อื่นๆ ที่ไม่เกี่ยวกับ "ข้อมูลไม่ถูกต้อง" เลย -For example: +ตัวอย่าง: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ข้อมูลไม่ครบ try { - user = JSON.parse(json); // <-- forgot to put "let" before user + user = JSON.parse(json); // <-- ลืมใส่ "let" หน้า user // ... } catch (err) { alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined - // (no JSON Error actually) + // (จริงๆ ไม่ใช่ JSON Error เลย) } ``` -Of course, everything's possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades -- suddenly a bug may be discovered that leads to terrible hacks. +เกิดขึ้นได้แน่นอน! โปรแกรมเมอร์ก็พลาดกัน แม้แต่ไลบรารีที่คนใช้เป็นล้านก็ยังมีบั๊กโผล่ทีหลังได้ -In our case, `try...catch` is placed to catch "incorrect data" errors. But by its nature, `catch` gets *all* errors from `try`. Here it gets an unexpected error, but still shows the same `"JSON Error"` message. That's wrong and also makes the code more difficult to debug. +ปัญหาคือ `catch` จะจับ error *ทุกชนิด* จาก `try` ไม่เลือกหน้า เลยจับ error ที่เราไม่ได้คาดไว้มาแสดงเป็น `"JSON Error"` ซึ่งผิด แถมทำให้ดีบักยากอีก -To avoid such problems, we can employ the "rethrowing" technique. The rule is simple: +ทางแก้คือเทคนิค "rethrowing" — หลักการง่ายมาก: -**Catch should only process errors that it knows and "rethrow" all others.** +**`catch` ควรจัดการแค่ error ที่รู้จัก ที่เหลือก็โยนต่อออกไป** -The "rethrowing" technique can be explained in more detail as: +ทำแบบนี้: -1. Catch gets all errors. -2. In the `catch (err) {...}` block we analyze the error object `err`. -3. If we don't know how to handle it, we do `throw err`. +1. `catch` รับ error มาทุกตัว +2. ในบล็อก `catch (err) {...}` เราวิเคราะห์ออบเจ็กต์ error `err` +3. ถ้าเป็น error ที่จัดการไม่เป็น ก็ `throw err` ออกไป -Usually, we can check the error type using the `instanceof` operator: +ใช้ `instanceof` เช็คชนิด error ได้: ```js run try { @@ -374,17 +362,17 @@ try { *!* if (err instanceof ReferenceError) { */!* - alert('ReferenceError'); // "ReferenceError" for accessing an undefined variable + alert('ReferenceError'); // "ReferenceError" — เข้าถึงตัวแปรที่ยังไม่ได้ประกาศ } } ``` -We can also get the error class name from `err.name` property. All native errors have it. Another option is to read `err.constructor.name`. +หรือจะดูจาก `err.name` หรือ `err.constructor.name` ก็ได้ -In the code below, we use rethrowing so that `catch` only handles `SyntaxError`: +ลองดูโค้ดที่ใช้ rethrowing — `catch` จัดการแค่ `SyntaxError` ที่เหลือโยนต่อออกไป: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ข้อมูลไม่ครบ try { let user = JSON.parse(json); @@ -394,7 +382,7 @@ try { } *!* - blabla(); // unexpected error + blabla(); // error ที่ไม่คาดคิด */!* alert( user.name ); @@ -405,18 +393,18 @@ try { if (err instanceof SyntaxError) { alert( "JSON Error: " + err.message ); } else { - throw err; // rethrow (*) + throw err; // โยนต่อ (*) } */!* } ``` -The error throwing on line `(*)` from inside `catch` block "falls out" of `try...catch` and can be either caught by an outer `try...catch` construct (if it exists), or it kills the script. +error ที่ throw ออกจาก `catch` ในบรรทัด `(*)` จะ "หลุด" ออกจาก `try...catch` นี้ ไปให้ `try...catch` ชั้นนอกจับ (ถ้ามี) หรือไม่ก็ทำให้สคริปต์ตาย -So the `catch` block actually handles only errors that it knows how to deal with and "skips" all others. +แบบนี้ `catch` จัดการแค่ error ที่รู้จัก ที่เหลือก็ปล่อยผ่านไป -The example below demonstrates how such errors can be caught by one more level of `try...catch`: +ลองดูตัวอย่างที่มี `try...catch` ซ้อน 2 ชั้น: ```js run function readData() { @@ -431,7 +419,7 @@ function readData() { // ... if (!(err instanceof SyntaxError)) { *!* - throw err; // rethrow (don't know how to deal with it) + throw err; // โยนต่อ (จัดการไม่เป็น) */!* } } @@ -441,42 +429,40 @@ try { readData(); } catch (err) { *!* - alert( "External catch got: " + err ); // caught it! + alert( "catch ชั้นนอกจับได้: " + err ); // จับได้แล้ว! */!* } ``` -Here `readData` only knows how to handle `SyntaxError`, while the outer `try...catch` knows how to handle everything. +`readData` จัดการได้แค่ `SyntaxError` — error อื่นๆ หลุดออกไปให้ `try...catch` ชั้นนอกจับแทน ## try...catch...finally -Wait, that's not all. +เดี๋ยวก่อน ยังมีอีกส่วนนะ — `finally` -The `try...catch` construct may have one more code clause: `finally`. +ถ้าเพิ่ม `finally` เข้าไป บล็อกนี้จะ **รันเสมอไม่ว่าจะเกิดอะไรขึ้น**: -If it exists, it runs in all cases: +- หลัง `try` ถ้าไม่มี error +- หลัง `catch` ถ้ามี error -- after `try`, if there were no errors, -- after `catch`, if there were errors. - -The extended syntax looks like this: +เขียนเต็มๆ เป็นแบบนี้: ```js *!*try*/!* { - ... try to execute the code ... + ... ลองรันโค้ด ... } *!*catch*/!* (err) { - ... handle errors ... + ... จัดการ error ... } *!*finally*/!* { - ... execute always ... + ... รันเสมอไม่ว่ายังไง ... } ``` -Try running this code: +ลองรันโค้ดนี้ดู: ```js run try { alert( 'try' ); - if (confirm('Make an error?')) BAD_CODE(); + if (confirm('จะให้เกิด error ไหม?')) BAD_CODE(); } catch (err) { alert( 'catch' ); } finally { @@ -484,27 +470,23 @@ try { } ``` -The code has two ways of execution: - -1. If you answer "Yes" to "Make an error?", then `try -> catch -> finally`. -2. If you say "No", then `try -> finally`. +โค้ดนี้มีการทำงานได้ 2 ทาง: -The `finally` clause is often used when we start doing something and want to finalize it in any case of outcome. +1. ถ้าตอบ "Yes" ที่ "จะให้เกิด error ไหม?" จะได้ `try -> catch -> finally` +2. ถ้าตอบ "No" จะได้ `try -> finally` -For instance, we want to measure the time that a Fibonacci numbers function `fib(n)` takes. Naturally, we can start measuring before it runs and finish afterwards. But what if there's an error during the function call? In particular, the implementation of `fib(n)` in the code below returns an error for negative or non-integer numbers. +`finally` เหมาะมากเวลาเริ่มทำอะไรแล้วต้องปิดงานให้เรียบร้อย ไม่ว่าจะสำเร็จหรือเจ๊ง -The `finally` clause is a great place to finish the measurements no matter what. - -Here `finally` guarantees that the time will be measured correctly in both situations -- in case of a successful execution of `fib` and in case of an error in it: +ตัวอย่าง — จับเวลาฟังก์ชันหา Fibonacci ถ้า `fib(n)` ได้ค่าติดลบหรือไม่ใช่จำนวนเต็มก็จะ throw error แต่เราก็ยังอยากรู้ว่าใช้เวลาเท่าไหร่อยู่ดีใช่ไหม? `finally` ช่วยได้เลย: ```js run -let num = +prompt("Enter a positive integer number?", 35) +let num = +prompt("ใส่จำนวนเต็มบวก", 35) let diff, result; function fib(n) { if (n < 0 || Math.trunc(n) != n) { - throw new Error("Must not be negative, and also an integer."); + throw new Error("ต้องไม่ติดลบ และต้องเป็นจำนวนเต็ม"); } return n <= 1 ? n : fib(n - 1) + fib(n - 2); } @@ -521,26 +503,24 @@ try { } */!* -alert(result || "error occurred"); +alert(result || "เกิด error"); -alert( `execution took ${diff}ms` ); +alert( `ใช้เวลา ${diff}ms` ); ``` -You can check by running the code with entering `35` into `prompt` -- it executes normally, `finally` after `try`. And then enter `-1` -- there will be an immediate error, and the execution will take `0ms`. Both measurements are done correctly. +ลองรันดู — ใส่ `35` ก็รันปกติ `finally` ทำงานหลัง `try` ใส่ `-1` ก็ error ทันทีแต่จับเวลาได้ถูกต้องเหมือนกัน ทั้งสองเคสผ่าน `finally` หมด -In other words, the function may finish with `return` or `throw`, that doesn't matter. The `finally` clause executes in both cases. +จะ `return` หรือ `throw` ก็ไม่สำคัญ — `finally` ทำงานทุกกรณี -```smart header="Variables are local inside `try...catch...finally`" -Please note that `result` and `diff` variables in the code above are declared *before* `try...catch`. +```smart header="ตัวแปรใน `try...catch...finally` เป็นตัวแปรภายในนะ" +สังเกตว่า `result` กับ `diff` ประกาศไว้ *ข้างนอก* `try...catch` -Otherwise, if we declared `let` in `try` block, it would only be visible inside of it. +เพราะถ้าประกาศ `let` ไว้ใน `try` จะใช้ได้แค่ข้างในบล็อกนั้นเท่านั้น ``` -````smart header="`finally` and `return`" -The `finally` clause works for *any* exit from `try...catch`. That includes an explicit `return`. - -In the example below, there's a `return` in `try`. In this case, `finally` is executed just before the control returns to the outer code. +````smart header="`finally` กับ `return`" +แม้จะ `return` ออกจาก `try` ก็ยังเข้า `finally` ก่อนเสมอ — `finally` รันก่อนที่จะคืนค่าออกไป ```js run function func() { @@ -559,40 +539,38 @@ function func() { } } -alert( func() ); // first works alert from finally, and then this one +alert( func() ); // alert จาก finally ขึ้นก่อน แล้วค่อยถึงอันนี้ ``` ```` ````smart header="`try...finally`" -The `try...finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors here (let them fall through), but want to be sure that processes that we started are finalized. +เขียนแค่ `try...finally` โดยไม่มี `catch` ก็ได้ — ใช้ตอนที่ไม่ต้องการจัดการ error ตรงนี้ แต่ต้องการปิดงานให้เรียบร้อย ```js function func() { - // start doing something that needs completion (like measurements) + // เริ่มทำอะไรบางอย่างที่ต้องปิดงานให้เรียบร้อย (เช่น จับเวลา) try { // ... } finally { - // complete that thing even if all dies + // ปิดงานให้เรียบร้อย แม้ทุกอย่างจะพัง } } ``` -In the code above, an error inside `try` always falls out, because there's no `catch`. But `finally` works before the execution flow leaves the function. +error ใน `try` จะหลุดออกไปเพราะไม่มี `catch` แต่ `finally` จะรันก่อนเสมอ ```` ## Global catch -```warn header="Environment-specific" -The information from this section is not a part of the core JavaScript. +```warn header="ขึ้นอยู่กับสภาพแวดล้อม" +เนื้อหาในส่วนนี้ไม่ได้เป็นส่วนหนึ่งของ JavaScript หลัก ``` -Let's imagine we've got a fatal error outside of `try...catch`, and the script died. Like a programming error or some other terrible thing. +แล้วถ้า error เกิดข้างนอก `try...catch` ล่ะ? สคริปต์ก็ตายเลยใช่ไหม? -Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages), etc. +จริงๆ มีทางดักจับได้นะ — ถึงแม้สเปก JavaScript ไม่ได้กำหนดไว้ แต่สภาพแวดล้อมส่วนใหญ่มีให้ใช้ เช่น Node.js มี [`process.on("uncaughtException")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) ส่วนในเบราว์เซอร์ก็มี [window.onerror](mdn:api/GlobalEventHandlers/onerror) ที่จะทำงานเมื่อมี error ที่ไม่ได้ถูกจับ -There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.js has [`process.on("uncaughtException")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to the special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property, that will run in case of an uncaught error. - -The syntax: +เขียนแบบนี้: ```js window.onerror = function(message, url, line, col, error) { @@ -601,75 +579,71 @@ window.onerror = function(message, url, line, col, error) { ``` `message` -: Error message. +: ข้อความ error `url` -: URL of the script where error happened. +: URL ของสคริปต์ที่เกิด error `line`, `col` -: Line and column numbers where error happened. +: หมายเลขบรรทัดและคอลัมน์ที่เกิด error `error` -: Error object. +: ออบเจ็กต์ error -For instance: +ตัวอย่าง: ```html run untrusted refresh height=1 ``` -The role of the global handler `window.onerror` is usually not to recover the script execution -- that's probably impossible in case of programming errors, but to send the error message to developers. - -There are also web-services that provide error-logging for such cases, like or . - -They work like this: +`window.onerror` ไม่ได้มีไว้กู้ชีวิตสคริปต์นะ (error จากโค้ดผิดก็กู้ไม่ได้อยู่แล้ว) แต่มีไว้ส่ง error ไปให้นักพัฒนารับรู้ -1. We register at the service and get a piece of JS (or a script URL) from them to insert on pages. -2. That JS script sets a custom `window.onerror` function. -3. When an error occurs, it sends a network request about it to the service. -4. We can log in to the service web interface and see errors. +มีเว็บเซอร์วิสที่ช่วยเก็บ error ให้ด้วย เช่น หรือ ทำงานแบบนี้: -## Summary +1. สมัครแล้วได้โค้ด JS มาแปะในเว็บ +2. โค้ดนั้นจะตั้ง `window.onerror` ให้ +3. พอเกิด error ก็ส่ง request ไปเก็บไว้ +4. เราเข้าไปดู error ได้ผ่านหน้าเว็บของเซอร์วิส -The `try...catch` construct allows to handle runtime errors. It literally allows to "try" running the code and "catch" errors that may occur in it. +## สรุป -The syntax is: +`try...catch` ช่วยให้เราจัดการ runtime error ได้ — "ลอง" รันโค้ดดู ถ้าเจ๊งก็ "จับ" error มาจัดการ ```js try { - // run this code + // รันโค้ดนี้ } catch (err) { - // if an error happened, then jump here - // err is the error object + // ถ้าเกิด error ให้กระโดดมาที่นี่ + // err คือออบเจ็กต์ error } finally { - // do in any case after try/catch + // รันเสมอหลังจาก try/catch } ``` -There may be no `catch` section or no `finally`, so shorter constructs `try...catch` and `try...finally` are also valid. +ไม่มี `catch` หรือไม่มี `finally` ก็ได้ — เขียนแค่ `try...catch` หรือ `try...finally` ก็ใช้ได้ -Error objects have following properties: +error object มีพร็อพเพอร์ตี้หลัก: -- `message` -- the human-readable error message. -- `name` -- the string with error name (error constructor name). -- `stack` (non-standard, but well-supported) -- the stack at the moment of error creation. +- `message` -- ข้อความ error +- `name` -- ชื่อ error (ชื่อคอนสตรักเตอร์) +- `stack` (ไม่มาตรฐาน แต่รองรับทั่วไป) -- call stack ตอนเกิด error -If an error object is not needed, we can omit it by using `catch {` instead of `catch (err) {`. +ไม่สน error details ก็เขียน `catch {` ไม่ต้องมี `(err)` ได้เลย -We can also generate our own errors using the `throw` operator. Technically, the argument of `throw` can be anything, but usually it's an error object inheriting from the built-in `Error` class. More on extending errors in the next chapter. +อยากสร้าง error เอง? ใช้ `throw` ได้เลย ส่วนใหญ่จะโยนออบเจ็กต์ที่สืบทอดจาก `Error` — อ่านเพิ่มได้ในบทถัดไป -*Rethrowing* is a very important pattern of error handling: a `catch` block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn't know. +*Rethrowing* เป็นเทคนิคสำคัญมาก: `catch` จัดการแค่ error ที่รู้จัก ที่เหลือโยนต่อออกไป -Even if we don't have `try...catch`, most environments allow us to setup a "global" error handler to catch errors that "fall out". In-browser, that's `window.onerror`. +ถึงไม่มี `try...catch` สภาพแวดล้อมส่วนใหญ่ก็มี global error handler ให้ใช้ ในเบราว์เซอร์ก็คือ `window.onerror` \ No newline at end of file diff --git a/1-js/10-error-handling/2-custom-errors/1-format-error/task.md b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md index 2c8e910fc..9ab7b338f 100644 --- a/1-js/10-error-handling/2-custom-errors/1-format-error/task.md +++ b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Inherit from SyntaxError +# สืบทอดจาก SyntaxError -Create a class `FormatError` that inherits from the built-in `SyntaxError` class. +สร้างคลาส `FormatError` ที่สืบทอดมาจากคลาส `SyntaxError` ซึ่งเป็นคลาส built-in -It should support `message`, `name` and `stack` properties. +คลาสนี้ต้องรองรับพร็อพเพอร์ตี้ `message`, `name` และ `stack` -Usage example: +ตัวอย่างการใช้งาน: ```js let err = new FormatError("formatting error"); @@ -18,5 +18,5 @@ alert( err.name ); // FormatError alert( err.stack ); // stack alert( err instanceof FormatError ); // true -alert( err instanceof SyntaxError ); // true (because inherits from SyntaxError) +alert( err instanceof SyntaxError ); // true (เพราะสืบทอดจาก SyntaxError) ``` diff --git a/1-js/10-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md index d28b07439..21f746aa1 100644 --- a/1-js/10-error-handling/2-custom-errors/article.md +++ b/1-js/10-error-handling/2-custom-errors/article.md @@ -1,42 +1,42 @@ -# Custom errors, extending Error +# สร้าง error class เอง ด้วยการ extend Error -When we develop something, we often need our own error classes to reflect specific things that may go wrong in our tasks. For errors in network operations we may need `HttpError`, for database operations `DbError`, for searching operations `NotFoundError` and so on. +ตอนพัฒนาแอปจริง เราจะต้องมี error class เป็นของตัวเอง เพื่อบอกว่าเกิดปัญหาอะไรขึ้นในแต่ละจุด เช่น error เรื่อง network อาจต้องใช้ `HttpError` เรื่องฐานข้อมูลก็ `DbError` หาข้อมูลไม่เจอก็ `NotFoundError` -Our errors should support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have a `statusCode` property with a value like `404` or `403` or `500`. +error ที่เราสร้างขึ้นเองก็ควรมีพร็อพเพอร์ตี้พื้นฐานอย่าง `message`, `name` และ `stack` ด้วย แต่ยังเพิ่มพร็อพเพอร์ตี้เฉพาะของตัวเองได้ เช่น ออบเจ็กต์ `HttpError` อาจมี `statusCode` เก็บค่า `404` หรือ `403` หรือ `500` -JavaScript allows to use `throw` with any argument, so technically our custom error classes don't need to inherit from `Error`. But if we inherit, then it becomes possible to use `obj instanceof Error` to identify error objects. So it's better to inherit from it. +JavaScript ยอมให้ throw อะไรก็ได้ — error class ที่สร้างเองจึงไม่จำเป็นต้อง extend จาก `Error` แต่ถ้า extend ไว้ เราจะเช็คด้วย `obj instanceof Error` ได้เลย เป็นท่าที่ดีกว่า -As the application grows, our own errors naturally form a hierarchy. For instance, `HttpTimeoutError` may inherit from `HttpError`, and so on. +พอแอปโตขึ้น error class ต่างๆ ก็จะค่อยๆ แตกกิ่งเป็นลำดับชั้นอย่างเป็นธรรมชาติ เช่น `HttpTimeoutError` extend จาก `HttpError` อีกที -## Extending Error +## ต่อยอดจาก Error -As an example, let's consider a function `readUser(json)` that should read JSON with user data. +ลองดูตัวอย่างจริงกัน สมมติเรามีฟังก์ชัน `readUser(json)` ที่รับ JSON แล้วอ่านข้อมูลผู้ใช้ออกมา -Here's an example of how a valid `json` may look: +JSON ที่ถูกต้องจะหน้าตาประมาณนี้: ```js let json = `{ "name": "John", "age": 30 }`; ``` -Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it throws `SyntaxError`. But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, it may not have `name` and `age` properties that are essential for our users. +ภายในฟังก์ชันเราใช้ `JSON.parse` ซึ่งถ้ารับ `json` ที่มีรูปแบบผิดจะ throw `SyntaxError` ออกมา แต่ถึง `json` จะถูกไวยากรณ์แล้ว ก็ไม่ได้แปลว่าข้อมูลถูกต้องเสมอไปนะ — อาจจะขาดฟิลด์สำคัญ เช่น ไม่มี `name` หรือ `age` ก็ได้ -Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field. +ฟังก์ชัน `readUser(json)` จะไม่ได้แค่อ่าน JSON อย่างเดียว แต่ต้องเช็ค ("validate") ข้อมูลด้วย ถ้าฟิลด์ที่จำเป็นหายไป หรือรูปแบบผิด ก็ถือว่าเป็น error — แต่ไม่ใช่ `SyntaxError` นะ เพราะตัว JSON ถูกไวยากรณ์อยู่ เป็น error คนละท่ากัน เราจะเรียกว่า `ValidationError` แล้วสร้างเป็นคลาสขึ้นมา error แบบนี้ควรบอกด้วยว่าฟิลด์ไหนมีปัญหา -Our `ValidationError` class should inherit from the `Error` class. +คลาส `ValidationError` ของเราควร extend จากคลาส `Error` -The `Error` class is built-in, but here's its approximate code so we can understand what we're extending: +คลาส `Error` เป็นคลาส built-in แต่ลองดูโค้ดจำลองกันก่อนว่าข้างในมีอะไรบ้าง: ```js -// The "pseudocode" for the built-in Error class defined by JavaScript itself +// "pseudocode" ของคลาส Error ที่ JavaScript สร้างไว้ให้ class Error { constructor(message) { this.message = message; - this.name = "Error"; // (different names for different built-in error classes) - this.stack = ; // non-standard, but most environments support it + this.name = "Error"; // (คลาส error ที่ built-in มาจะมีชื่อต่างกันไป) + this.stack = ; // ไม่ใช่มาตรฐาน แต่เกือบทุกเอนจินรองรับ } } ``` -Now let's inherit `ValidationError` from it and try it in action: +ทีนี้มาสร้าง `ValidationError` โดย extend จาก `Error` แล้วลองเรียกใช้ดู: ```js run *!* @@ -49,23 +49,23 @@ class ValidationError extends Error { } function test() { - throw new ValidationError("Whoops!"); + throw new ValidationError("อุ๊ปส์!"); } try { test(); } catch(err) { - alert(err.message); // Whoops! + alert(err.message); // อุ๊ปส์! alert(err.name); // ValidationError - alert(err.stack); // a list of nested calls with line numbers for each + alert(err.stack); // รายการเรียกฟังก์ชันซ้อนกัน พร้อมเลขบรรทัด } ``` -Please note: in the line `(1)` we call the parent constructor. JavaScript requires us to call `super` in the child constructor, so that's obligatory. The parent constructor sets the `message` property. +สังเกตบรรทัด `(1)` — เราเรียก `super` เพื่อเรียกคอนสตรักเตอร์ของคลาสแม่ JavaScript บังคับว่าคลาสลูกต้องเรียก `super` เสมอ ซึ่งคอนสตรักเตอร์ของคลาสแม่จะตั้งค่า `message` ให้ -The parent constructor also sets the `name` property to `"Error"`, so in the line `(2)` we reset it to the right value. +คลาสแม่ยังตั้ง `name` เป็น `"Error"` ด้วย เราเลยต้องเปลี่ยนเป็นค่าที่ถูกต้องในบรรทัด `(2)` -Let's try to use it in `readUser(json)`: +มาลองใช้กับ `readUser(json)` จริงๆ กัน: ```js run class ValidationError extends Error { @@ -75,57 +75,57 @@ class ValidationError extends Error { } } -// Usage +// ใช้งานจริง function readUser(json) { let user = JSON.parse(json); if (!user.age) { - throw new ValidationError("No field: age"); + throw new ValidationError("ไม่มีฟิลด์: age"); } if (!user.name) { - throw new ValidationError("No field: name"); + throw new ValidationError("ไม่มีฟิลด์: name"); } return user; } -// Working example with try..catch +// ตัวอย่างการใช้ try..catch try { let user = readUser('{ "age": 25 }'); } catch (err) { if (err instanceof ValidationError) { *!* - alert("Invalid data: " + err.message); // Invalid data: No field: name + alert("ข้อมูลไม่ถูกต้อง: " + err.message); // ข้อมูลไม่ถูกต้อง: ไม่มีฟิลด์: name */!* } else if (err instanceof SyntaxError) { // (*) alert("JSON Syntax Error: " + err.message); } else { - throw err; // unknown error, rethrow it (**) + throw err; // error ที่ไม่รู้จัก โยนต่อออกไป (**) } } ``` -The `try..catch` block in the code above handles both our `ValidationError` and the built-in `SyntaxError` from `JSON.parse`. +บล็อก `try..catch` ในโค้ดด้านบนจัดการได้ทั้ง `ValidationError` ที่เราสร้างเองและ `SyntaxError` ที่มาจาก `JSON.parse` -Please take a look at how we use `instanceof` to check for the specific error type in the line `(*)`. +ลองสังเกตบรรทัด `(*)` ดู — เราใช้ `instanceof` เพื่อเช็คว่า error เป็นชนิดไหน -We could also look at `err.name`, like this: +จะใช้ `err.name` แทนก็ได้ แบบนี้: ```js // ... -// instead of (err instanceof SyntaxError) +// แทนที่จะใช้ (err instanceof SyntaxError) } else if (err.name == "SyntaxError") { // (*) // ... ``` -The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof. +แต่ `instanceof` ดีกว่ามากนะ เพราะถ้าอนาคตเราแตกคลาสย่อยจาก `ValidationError` อีก เช่น `PropertyRequiredError` ท่า `instanceof` จะยังใช้ได้กับคลาสลูกทั้งหมด ถือว่ารองรับอนาคตดี -Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (caused by a typo in the code or other unknown reasons) should fall through. +จุดสำคัญอีกอย่าง — ถ้า `catch` เจอ error ที่ไม่รู้จัก ต้องโยนต่อออกไปเหมือนในบรรทัด `(**)` เพราะ `catch` ของเราจัดการได้แค่ validation error กับ syntax error เท่านั้น error ชนิดอื่น (เช่น พิมพ์ตัวแปรผิด หรือเหตุผลอื่นที่คาดไม่ถึง) ต้องปล่อยให้หลุดออกไป -## Further inheritance +## สืบทอดต่ออีกชั้น -The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age` instead of a number). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing. +คลาส `ValidationError` ของเราค่อนข้างกว้าง ปัญหาที่เจอได้มีหลายแบบ — พร็อพเพอร์ตี้อาจหายไป หรืออาจเป็นรูปแบบผิด (เช่น `age` เป็นสตริงแทนที่จะเป็นตัวเลข) มาสร้างคลาสที่เจาะจงกว่ากัน คือ `PropertyRequiredError` สำหรับกรณีที่พร็อพเพอร์ตี้หายไปโดยเฉพาะ พร้อมเก็บข้อมูลว่าพร็อพเพอร์ตี้ไหนที่หายไปด้วย ```js run class ValidationError extends Error { @@ -145,7 +145,7 @@ class PropertyRequiredError extends ValidationError { } */!* -// Usage +// ใช้งานจริง function readUser(json) { let user = JSON.parse(json); @@ -159,32 +159,32 @@ function readUser(json) { return user; } -// Working example with try..catch +// ตัวอย่างการใช้ try..catch try { let user = readUser('{ "age": 25 }'); } catch (err) { if (err instanceof ValidationError) { *!* - alert("Invalid data: " + err.message); // Invalid data: No property: name + alert("ข้อมูลไม่ถูกต้อง: " + err.message); // ข้อมูลไม่ถูกต้อง: No property: name alert(err.name); // PropertyRequiredError alert(err.property); // name */!* } else if (err instanceof SyntaxError) { alert("JSON Syntax Error: " + err.message); } else { - throw err; // unknown error, rethrow it + throw err; // error ที่ไม่รู้จัก โยนต่อออกไป } } ``` -The new class `PropertyRequiredError` is easy to use: we only need to pass the property name: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor. +คลาส `PropertyRequiredError` ใช้งานง่ายมาก — แค่ส่งชื่อพร็อพเพอร์ตี้เข้าไป: `new PropertyRequiredError(property)` ส่วน `message` ที่อ่านรู้เรื่องนั้น คอนสตรักเตอร์สร้างให้เอง -Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedious -- to assign `this.name = ` in every custom error class. We can avoid it by making our own "basic error" class that assigns `this.name = this.constructor.name`. And then inherit all our custom errors from it. +สังเกตไหมว่า `this.name` ใน `PropertyRequiredError` ก็ต้องกำหนดเองเหมือนกัน? ทำแบบนี้ทุกคลาสก็น่าเบื่อเหมือนกันนะ — ต้องมานั่งเขียน `this.name = <ชื่อคลาส>` ทุกครั้ง แก้ได้ง่ายๆ โดยสร้างคลาส "base error" ของเราเอง ที่กำหนด `this.name = this.constructor.name` ให้อัตโนมัติ แล้วให้ error class อื่นๆ extend จากมันอีกที -Let's call it `MyError`. +ลองตั้งชื่อว่า `MyError` ดู -Here's the code with `MyError` and other custom error classes, simplified: +โค้ดที่ใช้ `MyError` เป็นฐาน จะกระชับขึ้นแบบนี้: ```js run class MyError extends Error { @@ -205,51 +205,51 @@ class PropertyRequiredError extends ValidationError { } } -// name is correct +// name ถูกต้องเลย alert( new PropertyRequiredError("field").name ); // PropertyRequiredError ``` -Now custom errors are much shorter, especially `ValidationError`, as we got rid of the `"this.name = ..."` line in the constructor. +ตอนนี้ error class ที่สร้างเองสั้นลงมาก โดยเฉพาะ `ValidationError` ที่ไม่ต้องมีบรรทัด `"this.name = ..."` ในคอนสตรักเตอร์อีกแล้ว -## Wrapping exceptions +## การห่อหุ้ม exception (Wrapping exceptions) -The purpose of the function `readUser` in the code above is "to read the user data". There may occur different kinds of errors in the process. Right now we have `SyntaxError` and `ValidationError`, but in the future `readUser` function may grow and probably generate other kinds of errors. +ฟังก์ชัน `readUser` ข้างบนมีหน้าที่ "อ่านข้อมูลผู้ใช้" ระหว่างทำงานอาจเจ๊งได้หลายแบบ ตอนนี้เรามี `SyntaxError` กับ `ValidationError` แต่อนาคตฟังก์ชันนี้อาจเพิ่มเข้ามาอีก -The code which calls `readUser` should handle these errors. Right now it uses multiple `if`s in the `catch` block, that check the class and handle known errors and rethrow the unknown ones. +โค้ดที่เรียก `readUser` ต้องจัดการ error เหล่านี้ทั้งหมด ตอนนี้ใช้ `if` หลายตัวใน `catch` เพื่อเช็คทีละชนิด จัดการ error ที่รู้จัก ส่วนที่ไม่รู้จักก็โยนต่อ -The scheme is like this: +ท่าที่ใช้อยู่หน้าตาแบบนี้: ```js try { ... - readUser() // the potential error source + readUser() // แหล่งที่อาจเกิด error ... } catch (err) { if (err instanceof ValidationError) { - // handle validation errors + // จัดการ validation error } else if (err instanceof SyntaxError) { - // handle syntax errors + // จัดการ syntax error } else { - throw err; // unknown error, rethrow it + throw err; // error ที่ไม่รู้จัก โยนต่อ } } ``` -In the code above we can see two types of errors, but there can be more. +ในโค้ดนี้มี error 2 ชนิด แต่จริงๆ อาจมีมากกว่านี้ -If the `readUser` function generates several kinds of errors, then we should ask ourselves: do we really want to check for all error types one-by-one every time? +ถ้า `readUser` ยิง error ออกมาหลายชนิด ถามว่าเราอยากมานั่งเช็คทีละชนิดทุกครั้งจริงหรือเปล่า? -Often the answer is "No": we'd like to be "one level above all that". We just want to know if there was a "data reading error" -- why exactly it happened is often irrelevant (the error message describes it). Or, even better, we'd like to have a way to get the error details, but only if we need to. +ส่วนใหญ่คำตอบคือ "ไม่" — เราแค่อยากรู้ว่า "อ่านข้อมูลเจ๊ง" ส่วนรายละเอียดว่าเจ๊งเพราะอะไรนั้น ดูจาก error message ได้ หรือถ้าอยากรู้ลึกก็ค่อยเจาะเข้าไปดูทีหลัง -The technique that we describe here is called "wrapping exceptions". +เทคนิคนี้เรียกว่า "การห่อหุ้ม exception" (wrapping exceptions) -1. We'll make a new class `ReadError` to represent a generic "data reading" error. -2. The function `readUser` will catch data reading errors that occur inside it, such as `ValidationError` and `SyntaxError`, and generate a `ReadError` instead. -3. The `ReadError` object will keep the reference to the original error in its `cause` property. +1. สร้างคลาส `ReadError` ขึ้นมาเป็นตัวแทนของ error ทุกชนิดที่เกี่ยวกับ "การอ่านข้อมูล" +2. ฟังก์ชัน `readUser` จะจับ error ที่เกิดภายใน (ทั้ง `ValidationError` และ `SyntaxError`) แล้วสร้าง `ReadError` ขึ้นมาแทน +3. ออบเจ็กต์ `ReadError` จะเก็บ error ต้นทางไว้ในพร็อพเพอร์ตี้ `cause` -Then the code that calls `readUser` will only have to check for `ReadError`, not for every kind of data reading errors. And if it needs more details of an error, it can check its `cause` property. +โค้ดที่เรียก `readUser` ก็แค่เช็ค `ReadError` อย่างเดียว ไม่ต้องไล่เช็คทุกชนิดอีกต่อไป ถ้าต้องการรายละเอียดก็ดูจาก `cause` ได้เลย -Here's the code that defines `ReadError` and demonstrates its use in `readUser` and `try..catch`: +มาดูโค้ดเต็มๆ กัน: ```js run class ReadError extends Error { @@ -317,14 +317,14 @@ try { } ``` -In the code above, `readUser` works exactly as described -- catches syntax and validation errors and throws `ReadError` errors instead (unknown errors are rethrown as usual). +โค้ดนี้ `readUser` ทำตามที่อธิบายไว้ — จับ syntax error กับ validation error แล้ว throw เป็น `ReadError` แทน (error ที่ไม่รู้จักก็โยนต่อตามปกติ) -So the outer code checks `instanceof ReadError` and that's it. No need to list all possible error types. +โค้ดข้างนอกก็แค่เช็ค `instanceof ReadError` อย่างเดียวเป็นอันจบ ไม่ต้องมาไล่เช็คทุกชนิดของ error อีก -The approach is called "wrapping exceptions", because we take "low level" exceptions and "wrap" them into `ReadError` that is more abstract. It is widely used in object-oriented programming. +ท่านี้เรียกว่า "การห่อหุ้ม exception" (wrapping exceptions) — เอา error ระดับล่างมา "ห่อ" ไว้ใน `ReadError` ที่เป็น error ระดับสูงกว่า เป็นเทคนิคที่ใช้กันเยอะมากใน object-oriented programming -## Summary +## สรุป -- We can inherit from `Error` and other built-in error classes normally. We just need to take care of the `name` property and don't forget to call `super`. -- We can use `instanceof` to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from a 3rd-party library and there's no easy way to get its class. Then `name` property can be used for such checks. -- Wrapping exceptions is a widespread technique: a function handles low-level exceptions and creates higher-level errors instead of various low-level ones. Low-level exceptions sometimes become properties of that object like `err.cause` in the examples above, but that's not strictly required. +- เราสร้าง error class เองได้โดย extend จาก `Error` หรือคลาส built-in error อื่นๆ แค่อย่าลืมตั้ง `name` และเรียก `super` ให้เรียบร้อย +- ใช้ `instanceof` เช็คชนิดของ error ได้ ใช้กับคลาสลูกก็ได้เหมือนกัน แต่บางทีได้ error จากไลบรารีภายนอก ไม่มีทางเข้าถึงคลาส — ก็เช็คจากพร็อพเพอร์ตี้ `name` แทนได้ +- การห่อหุ้ม exception เป็นเทคนิคที่ใช้บ่อย — ฟังก์ชันจับ error ระดับล่าง แล้วสร้าง error ระดับสูงขึ้นมาแทน error ต้นทางจะเก็บไว้ในพร็อพเพอร์ตี้อย่าง `err.cause` แต่จะไม่เก็บก็ได้ ไม่ได้บังคับ diff --git a/1-js/10-error-handling/index.md b/1-js/10-error-handling/index.md index face61c6e..8e1f4a798 100644 --- a/1-js/10-error-handling/index.md +++ b/1-js/10-error-handling/index.md @@ -1 +1 @@ -# Error handling +# การจัดการข้อผิดพลาด (Error handling)