การแก้ไขข้อยกเว้นในเว็บแอปพลิเคชันดูเหมือนจะง่ายดายเพียงหยุดการดําเนินการชั่วคราวเมื่อเกิดข้อผิดพลาดและตรวจสอบ แต่ลักษณะแบบอะซิงโครนัสของ JavaScript ทําให้การดำเนินการนี้ซับซ้อนอย่างไม่น่าเชื่อ เครื่องมือสำหรับนักพัฒนาเว็บของ Chrome จะรู้ได้อย่างไรว่าจะหยุดชั่วคราวเมื่อใดและที่ไหนเมื่อมีข้อยกเว้นเกิดขึ้นกับ Promise และฟังก์ชันแบบแอซิงโครไนซ์
โพสต์นี้จะเจาะลึกความท้าทายของการคาดการณ์การจับ ซึ่งเป็นความสามารถของ DevTools ในการคาดการณ์ว่าจะมีข้อยกเว้นเกิดขึ้นในโค้ดของคุณในภายหลังหรือไม่ เราจะอธิบายสาเหตุที่การแก้ไขข้อบกพร่องเป็นเรื่องยากและวิธีที่การปรับปรุงล่าสุดใน V8 (เครื่องมือ JavaScript ที่ขับเคลื่อน Chrome) ทําให้การแก้ไขข้อบกพร่องแม่นยํายิ่งขึ้น ซึ่งส่งผลให้คุณได้รับประสบการณ์การแก้ไขข้อบกพร่องที่ราบรื่นขึ้น
เหตุผลที่การคาดคะเนการจับมีความสำคัญ
ในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome คุณจะมีตัวเลือกในการหยุดการเรียกใช้โค้ดชั่วคราวสำหรับข้อยกเว้นที่ตรวจไม่พบเท่านั้น โดยข้ามข้อยกเว้นที่ตรวจพบ
เบื้องหลังคือโปรแกรมแก้ไขข้อบกพร่องจะหยุดทันทีเมื่อมีข้อยกเว้นเกิดขึ้นเพื่อเก็บรักษาบริบท นี่เป็นการคาดการณ์เนื่องจากในตอนนี้ เราไม่สามารถทราบได้แน่ชัดว่าระบบจะจับข้อยกเว้นในโค้ดในภายหลังหรือไม่ โดยเฉพาะในสถานการณ์แบบไม่พร้อมกัน ความไม่แน่นอนนี้เกิดจากความยากลำบากในการคาดการณ์ลักษณะการทํางานของโปรแกรม ซึ่งคล้ายกับปัญหาการหยุดทำงาน
ลองพิจารณาตัวอย่างต่อไปนี้: ควรหยุดโปรแกรมแก้ไขข้อบกพร่องไว้ที่ใด (ดูคำตอบได้ในส่วนถัดไป)
async function inner() {
throw new Error(); // Should the debugger pause here?
}
async function outer() {
try {
const promise = inner();
// ...
await promise;
} catch (e) {
// ... or should the debugger pause here?
}
}
การหยุดชั่วคราวเมื่อพบข้อยกเว้นในโปรแกรมแก้ไขข้อบกพร่องอาจทำให้เกิดปัญหาและนำไปสู่การหยุดชะงักบ่อยครั้งและการข้ามไปยังโค้ดที่ไม่คุ้นเคย หากต้องการลดปัญหานี้ ให้เลือกแก้ไขข้อยกเว้นที่ตรวจไม่พบเท่านั้น ซึ่งมีโอกาสที่จะบ่งบอกถึงข้อบกพร่องจริงมากกว่า อย่างไรก็ตาม ข้อมูลนี้ขึ้นอยู่กับความแม่นยำของการคาดการณ์การจับ
การคาดการณ์ที่ไม่ถูกต้องทําให้ผู้ใช้ไม่พอใจ
- ผลลบลวง (การคาดการณ์ว่า "ไม่พบ" เมื่อระบบจะพบ) การหยุดที่ไม่จำเป็นในโปรแกรมแก้ไขข้อบกพร่อง
- ผลบวกลวง (การคาดการณ์ว่า "ตรวจพบ" เมื่อจะตรวจไม่พบ) พลาดโอกาสในการจับข้อผิดพลาดร้ายแรง ซึ่งอาจบังคับให้คุณแก้ไขข้อยกเว้นทั้งหมด รวมถึงข้อยกเว้นที่คาดไว้
อีกวิธีในการลดการหยุดชะงักระหว่างการแก้ไขข้อบกพร่องคือการใช้รายการที่ละเว้น ซึ่งจะป้องกันไม่ให้หยุดพักเมื่อพบข้อยกเว้นภายในโค้ดของบุคคลที่สามที่ระบุ อย่างไรก็ตาม การคาดการณ์การจับที่แม่นยำยังคงมีความสำคัญ หากข้อยกเว้นที่มาจากโค้ดของบุคคลที่สามหลบเลี่ยงและส่งผลต่อโค้ดของคุณเอง คุณจะต้องแก้ไขข้อบกพร่องได้
วิธีการทํางานของโค้ดแบบอะซิงโครนัส
พรอมิส, async
และ await
และรูปแบบแบบแอซิงโครนัสอื่นๆ อาจทําให้เกิดสถานการณ์ที่ข้อยกเว้นหรือการปฏิเสธอาจใช้เส้นทางการดําเนินการที่กําหนดได้ยากเมื่อมีการยกข้อยกเว้นขึ้นก่อนที่จะมีการจัดการ เนื่องจากระบบอาจไม่รอพรอมต์หรือเพิ่มตัวแฮนเดิลการจับข้อยกเว้นจนกว่าข้อยกเว้นจะเกิดขึ้นแล้ว มาดูตัวอย่างก่อนหน้านี้กัน
async function inner() {
throw new Error();
}
async function outer() {
try {
const promise = inner();
// ...
await promise;
} catch (e) {
// ...
}
}
ในตัวอย่างนี้ outer()
จะเรียก inner()
ก่อน ซึ่งจะแสดงข้อยกเว้นทันที จากข้อมูลนี้ โปรแกรมแก้ไขข้อบกพร่องจะสรุปได้ว่า inner()
จะแสดงผลพรอมต์ที่ถูกปฏิเสธ แต่ขณะนี้ไม่มีการดำเนินการใดๆ ที่รอหรือจัดการพรอมต์นั้น โปรแกรมแก้ไขข้อบกพร่องอาจเดาได้ว่า outer()
อาจจะรอและเดาว่าจะทำในบล็อก try
ปัจจุบัน จึงจัดการกับมันได้ แต่โปรแกรมแก้ไขข้อบกพร่องไม่สามารถแน่ใจได้จนกว่าระบบจะแสดงผล Promise ที่ปฏิเสธและถึงคำสั่ง await
ในท้ายที่สุด
โปรแกรมแก้ไขข้อบกพร่องไม่สามารถรับประกันได้ว่าการคาดการณ์การจับข้อบกพร่องจะถูกต้อง แต่จะใช้วิธีการเดาที่หลากหลายสำหรับรูปแบบการเขียนโค้ดทั่วไปเพื่อคาดการณ์อย่างถูกต้อง การทำความเข้าใจรูปแบบเหล่านี้จะช่วยให้คุณเข้าใจวิธีการทำงานของ Promise
ใน V8 Promise
ของ JavaScript จะแสดงเป็นออบเจ็กต์ที่อยู่ในสถานะใดสถานะหนึ่งต่อไปนี้ นั่นคือ ดำเนินการเสร็จสมบูรณ์ ปฏิเสธ หรือรอดำเนินการ หาก Promise อยู่ในสถานะ "ดำเนินการแล้ว" และคุณเรียกใช้เมธอด .then()
ระบบจะสร้าง Promise ที่รอดำเนินการใหม่และกำหนดเวลางานการตอบสนองของ Promise ใหม่ ซึ่งจะเรียกใช้ตัวแฮนเดิล จากนั้นตั้งค่า Promise ให้ดำเนินการแล้วด้วยผลลัพธ์ของตัวแฮนเดิล หรือตั้งค่าให้ปฏิเสธหากตัวแฮนเดิลแสดงข้อยกเว้น การดำเนินการเดียวกันนี้จะเกิดขึ้นหากคุณเรียกใช้เมธอด .catch()
ในสัญญาที่ให้ผลลัพธ์เป็นเท็จ ในทางตรงกันข้าม การเรียก .then()
ในพรอมต์ที่ถูกปฏิเสธหรือ .catch()
ในพรอมต์ที่สำเร็จแล้วจะแสดงผลพรอมต์ในสถานะเดิมและจะไม่เรียกใช้ตัวแฮนเดิล
พรอมต์ที่รอดำเนินการมีรายการรีแอ็กชัน โดยออบเจ็กต์รีแอ็กชันแต่ละรายการจะมีตัวแฮนเดิลการตอบสนองหรือตัวแฮนเดิลการปฏิเสธ (หรือทั้ง 2 อย่าง) และพรอมต์รีแอ็กชัน ดังนั้นการเรียก .then()
ในพรอมต์ที่รอดำเนินการจะเพิ่มรีแอ็กชันที่มีแฮนเดิลที่ดำเนินการแล้ว รวมถึงพรอมต์ใหม่ที่รอดำเนินการสำหรับพรอมต์รีแอ็กชัน ซึ่ง .then()
จะแสดงผล การเรียก .catch()
จะเพิ่มรีแอ็กชันที่คล้ายกัน แต่จะมีการแฮนเดิลการปฏิเสธ การเรียก .then()
ด้วยอาร์กิวเมนต์ 2 รายการจะสร้างการตอบสนองที่มีตัวแฮนเดิลทั้ง 2 รายการ และการเรียก .finally()
หรือรอพรอมต์จะเพิ่มการตอบสนองที่มีตัวแฮนเดิล 2 รายการซึ่งเป็นฟังก์ชันในตัวสำหรับการใช้ฟีเจอร์เหล่านี้โดยเฉพาะ
เมื่อ Promise ที่รอดำเนินการได้รับการดำเนินการหรือถูกปฏิเสธในท้ายที่สุด ระบบจะกำหนดเวลางานรีแอ็กชันสำหรับตัวแฮนเดิลทั้งหมดที่ดำเนินการแล้วหรือตัวแฮนเดิลทั้งหมดที่ถูกปฏิเสธ จากนั้นระบบจะอัปเดตการสัญญาความรู้สึกที่เกี่ยวข้อง ซึ่งอาจทริกเกอร์งานความรู้สึกของตนเอง
ตัวอย่าง
ลองดูโค้ดต่อไปนี้
return new Promise(() => {throw new Error();})
.then(() => console.log('Never happened'))
.catch(() => console.log('Caught'));
รหัสนี้อาจไม่ชัดเจนว่าเกี่ยวข้องกับออบเจ็กต์ Promise
3 รายการที่แตกต่างกัน โค้ดข้างต้นเทียบเท่ากับโค้ดต่อไปนี้
const promise1 = new Promise(() => {throw new Error();});
const promise2 = promise1.then(() => console.log('Never happened'));
const promise3 = promise2.catch(() => console.log('Caught'));
return promise3;
ในตัวอย่างนี้ ระบบจะทําตามขั้นตอนต่อไปนี้
- เรียกเครื่องมือสร้าง
Promise
- ระบบจะสร้าง
Promise
ใหม่ซึ่งรอดำเนินการ - ฟังก์ชันนิรนามจะทำงาน
- ระบบจะแสดงข้อยกเว้น เมื่อถึงจุดนี้ โปรแกรมแก้ไขข้อบกพร่องจะต้องตัดสินใจว่าจะหยุดหรือไม่
- ตัวสร้าง Promise จะจับข้อยกเว้นนี้ แล้วเปลี่ยนสถานะของ Promise เป็น
rejected
โดยตั้งค่าเป็นข้อผิดพลาดที่แสดง โดยจะแสดงผลพรอมต์นี้ซึ่งจัดเก็บไว้ในpromise1
.then()
ไม่ได้ตั้งเวลางานรีแอ็กชันเนื่องจากpromise1
อยู่ในสถานะrejected
แต่ระบบจะแสดงผลลัพธ์เป็น Promise ใหม่ (promise2
) ซึ่งอยู่ในสถานะถูกปฏิเสธด้วยข้อผิดพลาดเดียวกัน.catch()
กําหนดเวลางานรีแอ็กชันด้วยตัวแฮนเดิลที่ระบุและสัญญารีแอ็กชันใหม่ซึ่งรอดําเนินการอยู่ ซึ่งจะแสดงผลเป็นpromise3
เมื่อถึงจุดนี้ โปรแกรมแก้ไขข้อบกพร่องจะทราบว่าระบบจะจัดการข้อผิดพลาด- เมื่องานรีแอ็กชันทำงาน ตัวแฮนเดิลจะแสดงผลตามปกติและสถานะของ
promise3
จะเปลี่ยนเป็นfulfilled
ตัวอย่างถัดไปมีโครงสร้างคล้ายกัน แต่การดําเนินการค่อนข้างแตกต่างกัน
return Promise.resolve()
.then(() => {throw new Error();})
.then(() => console.log('Never happened'))
.catch(() => console.log('Caught'));
ซึ่งเทียบเท่ากับ
const promise1 = Promise.resolve();
const promise2 = promise1.then(() => {throw new Error();});
const promise3 = promise2.then(() => console.log('Never happened'));
const promise4 = promise3.catch(() => console.log('Caught'));
return promise4;
ในตัวอย่างนี้ ระบบจะทําตามขั้นตอนต่อไปนี้
- ระบบจะสร้าง
Promise
ในสถานะfulfilled
และจัดเก็บไว้ในpromise1
- ระบบจะกำหนดเวลางานรีแอ็กชันแบบพรอมิสด้วยฟังก์ชันที่ไม่ระบุชื่อรายการแรก และระบบจะแสดงผลพรอมิสรีแอ็กชัน
(pending)
ของฟังก์ชันดังกล่าวเป็นpromise2
- ระบบจะเพิ่มรีแอ็กชันลงใน
promise2
ด้วยแฮนเดิลที่ดำเนินการเสร็จสมบูรณ์และสัญญารีแอ็กชัน ซึ่งแสดงผลเป็นpromise3
- ระบบเพิ่มรีแอ็กชันลงใน
promise3
ด้วยตัวแฮนเดิลที่ถูกปฏิเสธและพรอมต์รีแอ็กชันอีกรายการ ซึ่งแสดงผลเป็นpromise4
- ระบบจะเรียกใช้งานที่แสดงความรู้สึกซึ่งกำหนดเวลาไว้ในระยะที่ 2
- แฮนเดิลส่งข้อยกเว้น เมื่อถึงจุดนี้ โปรแกรมแก้ไขข้อบกพร่องจะต้องตัดสินใจว่าจะหยุดหรือไม่ ปัจจุบันตัวแฮนเดิลเป็นโค้ด JavaScript เพียงรายการเดียวที่ทำงานอยู่
- เนื่องจากงานสิ้นสุดด้วยข้อยกเว้น ระบบจึงตั้งค่าพรอมต์การรีแอ็กชันที่เกี่ยวข้อง (
promise2
) เป็นสถานะปฏิเสธโดยตั้งค่าเป็นข้อผิดพลาดที่แสดง - เนื่องจาก
promise2
มีรีแอ็กชัน 1 รายการ และรีแอ็กชันนั้นไม่มีตัวแฮนเดิลที่ถูกปฏิเสธ ระบบจึงตั้งค่าสัญญารีแอ็กชัน (promise3
) เป็นrejected
ด้วยข้อผิดพลาดเดียวกัน - เนื่องจาก
promise3
มีรีแอ็กชัน 1 รายการ และรีแอ็กชันดังกล่าวมีตัวแฮนเดิลที่ถูกปฏิเสธ ระบบจึงกำหนดเวลางานรีแอ็กชันตามสัญญาให้กับตัวแฮนเดิลดังกล่าวและสัญญารีแอ็กชัน (promise4
) - เมื่องานการตอบสนองนั้นทำงาน ตัวแฮนเดิลจะแสดงผลตามปกติและสถานะของ
promise4
จะเปลี่ยนเป็น "ดำเนินการแล้ว"
วิธีการคาดการณ์การจับ
แหล่งข้อมูลสำหรับการคาดการณ์การจับปลามี 2 แหล่ง รายการแรกคือสแต็กการเรียกใช้ ซึ่งเหมาะสําหรับข้อยกเว้นแบบซิงค์: โปรแกรมแก้ไขข้อบกพร่องจะเดินสแต็กการเรียกใช้ในลักษณะเดียวกับที่โค้ดการเลิกทำข้อยกเว้นจะทํา และหยุดหากพบเฟรมที่อยู่ในบล็อก try...catch
สําหรับพรอมิสหรือข้อยกเว้นที่ถูกปฏิเสธในคอนสตรัคเตอร์พรอมิสหรือในฟังก์ชันแบบแอสซิงโครนัสที่ไม่เคยถูกระงับ โปรแกรมแก้ไขข้อบกพร่องจะอาศัยสแต็กการเรียกใช้ด้วย แต่ในกรณีนี้ การคาดการณ์ของโปรแกรมแก้ไขข้อบกพร่องอาจไม่น่าเชื่อถือในบางกรณี เนื่องจากโค้ดแบบไม่พร้อมกันจะแสดงข้อยกเว้นที่ถูกปฏิเสธแทนที่จะแสดงข้อยกเว้นไปยังตัวแฮนเดิลที่ใกล้ที่สุด และโปรแกรมแก้ไขข้อบกพร่องต้องคาดเดาสิ่งที่ผู้เรียกใช้จะทำกับข้อยกเว้นดังกล่าว
ขั้นแรก โปรแกรมแก้ไขข้อบกพร่องจะถือว่าฟังก์ชันที่ได้รับพรอมต์ที่แสดงผลมีแนวโน้มที่จะแสดงผลพรอมต์นั้นหรือพรอมต์ที่มาจากพรอมต์นั้น เพื่อให้ฟังก์ชันแบบแอสซิงโครนัสที่อยู่สูงขึ้นในกองมีโอกาสรอพรอมต์ดังกล่าว ประการที่ 2 โปรแกรมแก้ไขข้อบกพร่องจะถือว่าหากมีการส่งคืนพรอมิสไปยังฟังก์ชันแบบแอซิงโครนัส โปรแกรมจะรอพรอมิสนั้นในไม่ช้าโดยไม่ต้องเข้าหรือออกจากบล็อก try...catch
ก่อน เราไม่รับประกันว่าข้อสันนิษฐานเหล่านี้จะถูกต้อง แต่เพียงพอที่จะทําการคาดการณ์ที่ถูกต้องสําหรับรูปแบบการเขียนโค้ดที่พบบ่อยที่สุดด้วยฟังก์ชันแบบไม่สอดคล้อง ใน Chrome เวอร์ชัน 125 เราได้เพิ่มวิธีการเฮิวริสติกอีกวิธีหนึ่ง โดยโปรแกรมแก้ไขข้อบกพร่องจะตรวจสอบว่าผู้เรียกกำลังจะเรียก .catch()
ในค่าที่จะแสดงผล (หรือ .then()
ที่มีอาร์กิวเมนต์ 2 รายการ หรือชุดการเรียก .then()
หรือ .finally()
ตามด้วย .catch()
หรือ .then()
ที่มีอาร์กิวเมนต์ 2 รายการ) หรือไม่ ในกรณีนี้ โปรแกรมแก้ไขข้อบกพร่องจะถือว่าเมธอดเหล่านี้เป็นเมธอดในสัญญาที่เรากำลังติดตามหรือเมธอดที่เกี่ยวข้องกับสัญญาดังกล่าว จึงจะจับการปฏิเสธได้
แหล่งข้อมูลที่สองคือต้นไม้ของปฏิกิริยาของ Promise โปรแกรมแก้ไขข้อบกพร่องเริ่มต้นด้วยพรอมต์รูท บางครั้งอาจเป็นสัญญาที่เพิ่งเรียกใช้เมธอด reject()
กรณีที่พบบ่อยกว่าคือเมื่อมีข้อยกเว้นหรือการปฏิเสธเกิดขึ้นระหว่างงานการตอบสนองของพรอมต์ และดูเหมือนว่าไม่มีรายการใดในสแต็กการเรียกที่จับข้อยกเว้นหรือปฏิเสธดังกล่าว โปรแกรมแก้ไขข้อบกพร่องจะติดตามจากพรอมต์ที่เชื่อมโยงกับการตอบสนอง โปรแกรมแก้ไขข้อบกพร่องจะดูการตอบสนองทั้งหมดในสัญญาที่รอดำเนินการและดูว่ามีการแฮนเดิลการปฏิเสธหรือไม่ หากรีแอ็กชันใดไม่ตรงกัน ระบบจะดูที่สัญญารีแอ็กชันและติดตามจากสัญญานั้นแบบย้อนกลับ หากการตอบสนองทั้งหมดนำไปสู่ตัวแฮนเดิลการปฏิเสธในท้ายที่สุด โปรแกรมแก้ไขข้อบกพร่องจะถือว่ามีการจับการปฏิเสธสัญญา ในกรณีพิเศษบางกรณี เช่น ไม่นับตัวแฮนเดิลการปฏิเสธในตัวสําหรับการเรียก .finally()
แผนภูมิการตอบสนองตามการคาดหวังเป็นแหล่งข้อมูลที่เชื่อถือได้หากมีข้อมูลดังกล่าว ในบางกรณี เช่น การเรียก Promise.reject()
หรือในเครื่องมือสร้าง Promise
หรือในฟังก์ชันแบบแอสซิงค์ที่ยังไม่ได้รออะไรเลย จะไม่มีปฏิกิริยาต่อการติดตาม และโปรแกรมแก้ไขข้อบกพร่องจะต้องใช้กองคําสั่งการเรียกเท่านั้น ในกรณีอื่นๆ โดยทั่วไปแล้วต้นไม้การตอบสนองของ Promise จะมีตัวแฮนเดิลที่จําเป็นต่อการอนุมานการคาดการณ์การจับข้อยกเว้น แต่อาจมีการเพิ่มตัวแฮนเดิลเพิ่มเติมในภายหลังซึ่งจะเปลี่ยนข้อยกเว้นจากการจับเป็นไม่จับ หรือในทางกลับกัน นอกจากนี้ยังมีการสัญญา เช่น การสัญญาที่ Promise.all/any/race
สร้างขึ้น ซึ่งการสัญญาอื่นๆ ในกลุ่มอาจส่งผลต่อวิธีจัดการการปฏิเสธ สําหรับเมธอดเหล่านี้ โปรแกรมแก้ไขข้อบกพร่องจะถือว่าระบบจะส่งต่อการปฏิเสธสัญญาหากสัญญาดังกล่าวยังรอดําเนินการอยู่
มาดูตัวอย่าง 2 ตัวอย่างต่อไปนี้
แม้ว่าตัวอย่างข้อยกเว้นที่พบ 2 รายการนี้ดูคล้ายกัน แต่ต้องใช้ heuristics การคาดการณ์การจับที่แตกต่างกันมาก ในตัวอย่างแรก ระบบจะสร้าง Promise ที่แก้ไขแล้ว จากนั้นกําหนดเวลางานรีแอ็กชันสําหรับ .then()
ซึ่งจะแสดงข้อยกเว้น จากนั้นเรียก .catch()
เพื่อแนบตัวแฮนเดิลการปฏิเสธไปยัง Promise รีแอ็กชัน เมื่อเรียกใช้งานการตอบสนอง ระบบจะแสดงข้อยกเว้น และต้นไม้การตอบสนองของ Promise จะมีตัวแฮนเดิลการจับ ดังนั้นระบบจะตรวจพบว่ามีการจับข้อยกเว้น ในตัวอย่างที่ 2 ระบบจะปฏิเสธ Promise ทันทีก่อนที่จะเรียกใช้โค้ดเพื่อเพิ่มตัวแฮนเดิลการจับ ดังนั้นจึงไม่มีตัวแฮนเดิลการปฏิเสธในต้นไม้การตอบสนองของ Promise โปรแกรมแก้ไขข้อบกพร่องต้องดูสแต็กการเรียก แต่ไม่มีบล็อก try...catch
ด้วย โปรแกรมแก้ไขข้อบกพร่องจะสแกนตำแหน่งก่อนหน้าตำแหน่งปัจจุบันในโค้ดเพื่อค้นหาการเรียกใช้ .catch()
และคาดการณ์ว่าระบบจะจัดการการปฏิเสธในท้ายที่สุด เพื่อคาดการณ์ได้อย่างถูกต้อง
สรุป
เราหวังว่าคําอธิบายนี้จะช่วยให้ทราบวิธีการทํางานของฟีเจอร์การคาดคะเนการจับภาพในเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome, จุดแข็ง และข้อจํากัด หากพบปัญหาในการแก้ไขข้อบกพร่องเนื่องจากการคาดการณ์ไม่ถูกต้อง ให้พิจารณาตัวเลือกต่อไปนี้
- เปลี่ยนรูปแบบการเขียนโค้ดให้คาดการณ์ได้ง่ายขึ้น เช่น การใช้ฟังก์ชันแบบแอ็กซิงคิวทีฟ
- เลือกหยุดที่ข้อยกเว้นทั้งหมดหากเครื่องมือสำหรับนักพัฒนาเว็บหยุดทำงานไม่สำเร็จเมื่อถึงเวลา
- ใช้เบรกพอยต์ "อย่าหยุดชั่วคราวที่นี่เลย" หรือเบรกพอยต์แบบมีเงื่อนไขหากโปรแกรมแก้ไขข้อบกพร่องหยุดอยู่ที่ตำแหน่งที่คุณไม่ต้องการให้หยุด
ขอขอบคุณ
ขอขอบคุณ Sofia Emelianova และ Jecelyn Yeen เป็นอย่างยิ่งสำหรับความช่วยเหลืออันล้ำค่าในการแก้ไขโพสต์นี้