ไม่ว่าระดับทักษะข้อผิดพลาดหรือข้อยกเว้นจะเป็นส่วนหนึ่งของชีวิตนักพัฒนาแอปพลิเคชันของคุณ ความไม่สอดคล้องกันของการพัฒนาเว็บทำให้หลายสถานที่ที่เกิดข้อผิดพลาดและเกิดขึ้นได้ กุญแจสำคัญในการแก้ปัญหาคือการจัดการกับข้อผิดพลาดที่ไม่คาดคิด (หรือข้อผิดพลาดที่คาดการณ์ได้) เพื่อควบคุมประสบการณ์ของผู้ใช้ ด้วย JavaScript มีคุณสมบัติทางเทคนิคและภาษาที่หลากหลายที่สามารถใช้ในการแก้ปัญหาได้อย่างถูกต้อง
มันเป็นอันตรายที่จะจัดการกับข้อผิดพลาดใน JavaScript หากคุณเชื่อในกฎหมายของเมอร์ฟีสิ่งที่ผิดพลาดในที่สุดจะผิดพลาดในที่สุด! ในบทความนี้ฉันจะขุดลงในการจัดการข้อผิดพลาดใน JavaScript ฉันจะเกี่ยวข้องกับข้อผิดพลาดและการปฏิบัติที่ดี ในที่สุดเราจะหารือเกี่ยวกับการประมวลผลรหัสแบบอะซิงโครนัสและ AJAX
ฉันคิดว่ารูปแบบที่ขับเคลื่อนด้วยเหตุการณ์ของ JavaScript เพิ่มความหมายที่หลากหลายให้กับภาษานี้ ฉันคิดว่าไม่มีความแตกต่างระหว่างเครื่องยนต์ที่ขับเคลื่อนด้วยเหตุการณ์และกลไกการรายงานข้อผิดพลาดของเบราว์เซอร์ประเภทนี้ เมื่อใดก็ตามที่เกิดข้อผิดพลาดจะเทียบเท่ากับการขว้างเหตุการณ์ ณ เวลาหนึ่งเวลา ในทางทฤษฎีเราสามารถจัดการกับข้อผิดพลาดในการขว้างเหตุการณ์ในจาวาสคริปต์เช่นเราจัดการกับเหตุการณ์ทั่วไป หากสิ่งนี้ฟังดูแปลกสำหรับคุณให้มุ่งเน้นไปที่การเริ่มต้นการเดินทางด้านล่าง บทความนี้กำหนดเป้าหมายเฉพาะ JavaScript ของลูกค้า
ตัวอย่าง
ตัวอย่างรหัสที่ใช้ในบทความนี้สามารถรับได้ใน GitHub หน้าปัจจุบันมีลักษณะเช่นนี้:
การคลิกแต่ละปุ่มจะทำให้เกิดข้อผิดพลาด มันจำลองข้อยกเว้นของประเภท typeerror ต่อไปนี้คือคำจำกัดความและการทดสอบหน่วยของโมดูลดังกล่าว
ฟังก์ชั่นข้อผิดพลาด () {var foo = {}; return foo.bar ();}ก่อนอื่นฟังก์ชั่นนี้จะกำหนด foo วัตถุเปล่า โปรดทราบว่าไม่ได้กำหนดวิธี Bar () ทุกที่ เราใช้การทดสอบหน่วยเพื่อตรวจสอบว่าสิ่งนี้จะทำให้เกิดข้อผิดพลาด
มัน ('พ่น typeError', function () {ควรจะเป็น (target, typeError);});การทดสอบหน่วยนี้ใช้การทดสอบการทดสอบจากห้องสมุดมอคค่าและควรใช้ไลบรารี Mocha เป็นกรอบการทดสอบที่ใช้งานและควร. js เป็นห้องสมุดยืนยัน หากคุณไม่คุ้นเคยกับพวกเขาคุณสามารถเรียกดูเอกสารออนไลน์ได้ฟรี กรณีทดสอบมักจะเริ่มต้นด้วย ('คำอธิบาย') และจบลงด้วยการผ่านหรือความล้มเหลวของการยืนยันในควร ข้อดีของการใช้เฟรมเวิร์กนี้คือสามารถทดสอบหน่วยในโหนดมากกว่าในเบราว์เซอร์ ฉันขอแนะนำให้คุณทำการทดสอบเหล่านี้อย่างจริงจังเพราะพวกเขาตรวจสอบแนวคิดพื้นฐานที่สำคัญมากมายใน JavaScript
ดังที่แสดงไว้ข้างต้นข้อผิดพลาด () กำหนดวัตถุที่ว่างเปล่าจากนั้นพยายามเรียกวิธีการในนั้น เนื่องจากเมธอดบาร์ () ไม่มีอยู่ในวัตถุนี้จึงจะทำการยกเว้น เชื่อใจฉันในภาษาไดนามิกเช่น JavaScript ทุกคนสามารถทำผิดพลาดได้
การสาธิตที่ไม่ดี
มาดูวิธีการจัดการข้อผิดพลาดที่ไม่ดี ฉันจะเป็นนามธรรมการกระทำที่ผิดและผูกไว้กับปุ่ม นี่คือสิ่งที่การทดสอบหน่วยของตัวจัดการดูเหมือน:
ฟังก์ชั่น badhandler (fn) {ลอง {return fn (); } catch (e) {} return null;}ฟังก์ชั่นการประมวลผลนี้ได้รับฟังก์ชั่นการโทรกลับ FN เป็นการพึ่งพา จากนั้นฟังก์ชั่นจะถูกเรียกภายในตัวจัดการ การทดสอบหน่วยนี้ตัวอย่างวิธีการใช้วิธีนี้
มัน ('ส่งคืนค่าโดยไม่มีข้อผิดพลาด', function () {var fn = function () {return 1;}; var result = target (fn); ผลลัพธ์ควรค่าเท่ากัน (1);}); มัน ('ส่งคืนค่า null กับข้อผิดพลาด', ฟังก์ชัน () {var fn = ฟังก์ชัน () ควร (ผลลัพธ์). เท่ากัน (null);});อย่างที่คุณเห็นหากเกิดข้อผิดพลาดวิธีการจัดการแปลก ๆ นี้จะส่งคืนค่าว่าง ฟังก์ชั่นการโทรกลับ FN () นี้จะชี้ไปที่วิธีการทางกฎหมายหรือข้อผิดพลาด เหตุการณ์การคลิกด้านล่างจะเสร็จสิ้นส่วนที่เหลือ
(ฟังก์ชั่น (handler, bomb) {var badbutton = document.getElementById ('bad'); ถ้า (badbutton) {badbutton.addeventListener ('คลิก', ฟังก์ชั่น () {Handler (ระเบิด); console.log (ลองนึกภาพสิ่งที่ไม่ดีคือสิ่งที่ฉันเพิ่งได้รับคือโมฆะ สิ่งนี้ทำให้ฉันสับสนมากเมื่อฉันกำลังคิดที่จะพิจารณาว่ามีอะไรผิดปกติ กลยุทธ์แห่งความเงียบเมื่อเกิดข้อผิดพลาดครอบคลุมทุกลิงก์จากการออกแบบประสบการณ์ผู้ใช้ไปจนถึงการทุจริตข้อมูล ด้านที่น่าหงุดหงิดตามมาคือฉันต้องใช้เวลาหลายชั่วโมงในการดีบัก แต่ไม่เห็นข้อผิดพลาดในบล็อกโค้ดที่จับได้ การประมวลผลแปลก ๆ นี้ซ่อนข้อผิดพลาดทั้งหมดในรหัสและสันนิษฐานว่าทุกอย่างเป็นเรื่องปกติ สิ่งนี้สามารถดำเนินการได้อย่างราบรื่นในบางทีมที่ไม่ใส่ใจกับคุณภาพของรหัส อย่างไรก็ตามข้อผิดพลาดที่ซ่อนอยู่เหล่านี้ในที่สุดจะบังคับให้คุณใช้เวลาหลายชั่วโมงในการดีบักรหัสของคุณ ในโซลูชันหลายชั้นที่ต้องอาศัยสแต็กการโทรเป็นไปได้ที่จะกำหนดว่าข้อผิดพลาดมาจากไหน มันอาจจะเหมาะสมที่จะจัดการกับการจับลองในบางกรณีอย่างเงียบ ๆ แต่ถ้าคุณพบข้อผิดพลาดคุณจะจัดการกับมันซึ่งไม่ใช่ทางออกที่ดี
ความล้มเหลวนี้คือกลยุทธ์ความเงียบจะแจ้งให้คุณจัดการกับข้อผิดพลาดในรหัสของคุณได้ดีขึ้น JavaScript เป็นวิธีที่สง่างามมากขึ้นในการจัดการกับปัญหาประเภทนี้
ไม่ใช่วิธีแก้ปัญหาที่อ่านได้
มาดูกันเถอะมาดูวิธีที่ยากต่อการทำความเข้าใจ ฉันจะข้ามส่วนคู่กับ DOM อย่างแน่นหนา ส่วนนี้ไม่แตกต่างจากวิธีที่ไม่ดีที่เราเพิ่งเห็น โฟกัสอยู่ที่ส่วนของการทดสอบหน่วยด้านล่างที่จัดการกับข้อยกเว้น
ฟังก์ชั่น uglyHandler (fn) {ลอง {return fn (); } catch (e) {โยนข้อผิดพลาด ('ข้อผิดพลาดใหม่'); }} it ('ส่งคืนข้อผิดพลาดใหม่พร้อมข้อผิดพลาด', function () {var fn = function () {โยน typeError ใหม่ ('ข้อผิดพลาดประเภท');}; ควรเข้าด้วยกัน (ฟังก์ชั่น () {เป้าหมาย (fn);}, ข้อผิดพลาด);});มีการปรับปรุงที่ดีเมื่อเทียบกับการรักษาที่ไม่ดีในตอนนี้ ข้อยกเว้นถูกโยนลงในสแต็คการโทร สิ่งที่ฉันชอบคือข้อผิดพลาดได้รับการปลดปล่อยจากสแต็กซึ่งเป็นประโยชน์อย่างมากสำหรับการดีบัก เมื่อมีการโยนข้อยกเว้นล่ามจะดูฟังก์ชั่นการประมวลผลถัดไปที่ระดับในสแต็กการโทร สิ่งนี้ให้โอกาสมากมายในการจัดการกับข้อผิดพลาดที่ระดับสูงสุดของสแต็คการโทร น่าเสียดายเพราะเขาเป็นความผิดพลาดที่ยากลำบากฉันไม่เห็นข้อมูลข้อผิดพลาดเดิม ดังนั้นฉันต้องดูสแต็คการโทรและค้นหาข้อยกเว้นดั้งเดิมที่สุด แต่อย่างน้อยฉันก็รู้ว่ามีข้อผิดพลาดเกิดขึ้นเมื่อข้อยกเว้นถูกโยนลงไป
แม้ว่าการจัดการข้อผิดพลาดที่อ่านไม่ออกนี้จะไร้เดียงสา แต่ก็ทำให้รหัสเข้าใจยาก มาดูกันว่าเบราว์เซอร์จัดการกับข้อผิดพลาดอย่างไร
โทรหาสแต็ก
จากนั้นวิธีหนึ่งในการโยนข้อยกเว้นคือการเพิ่มการลอง ... บล็อกรหัสจับที่ระดับบนสุดของสแต็กการโทร ตัวอย่างเช่น:
ฟังก์ชั่นหลัก (ระเบิด) {ลอง {bomb (); } catch (e) {// จัดการกับข้อผิดพลาดทั้งหมด}}แต่โปรดจำไว้ว่าฉันบอกว่าเบราว์เซอร์นั้นขับเคลื่อนด้วยเหตุการณ์หรือไม่? ใช่ข้อยกเว้นใน JavaScript นั้นไม่มีอะไรมากไปกว่าเหตุการณ์ ล่ามหยุดโปรแกรมที่บริบทที่มีข้อยกเว้นเกิดขึ้นในปัจจุบันและโยนข้อยกเว้น เพื่อยืนยันสิ่งนี้ต่อไปนี้เป็นฟังก์ชั่นการจัดการเหตุการณ์ทั่วโลกที่เราเห็น ดูเหมือนว่า:
window.addeventListener ('ข้อผิดพลาด', ฟังก์ชัน (e) {var error = e.error; console.log (ข้อผิดพลาด);});ตัวจัดการเหตุการณ์นี้จับข้อผิดพลาดในสภาพแวดล้อมการดำเนินการ เหตุการณ์ข้อผิดพลาดจะสร้างข้อผิดพลาดต่าง ๆ ในสถานที่ต่าง ๆ จุดของวิธีการนี้คือการจัดการกับข้อผิดพลาดส่วนกลางในรหัส เช่นเดียวกับเหตุการณ์อื่น ๆ คุณสามารถใช้ตัวจัดการระดับโลกเพื่อจัดการกับข้อผิดพลาดต่าง ๆ สิ่งนี้ทำให้การจัดการข้อผิดพลาดมีเป้าหมายเพียงอย่างเดียวหากคุณปฏิบัติตามความรับผิดชอบ (ความรับผิดชอบเดี่ยว, ปิดปิด, การทดแทน Liskov, การแยกอินเทอร์เฟซและการผกผันของการพึ่งพา) คุณสามารถลงทะเบียนฟังก์ชั่นการจัดการข้อผิดพลาดได้ตลอดเวลา ล่ามดำเนินการฟังก์ชั่นเหล่านี้ลูป รหัสได้รับการปลดปล่อยจากข้อความที่เต็มไปด้วยการลอง ... จับได้ง่ายต่อการดีบัก กุญแจสำคัญในวิธีการนี้คือการจัดการกับข้อผิดพลาดที่เกิดขึ้นเช่นเดียวกับเหตุการณ์ JavaScript ปกติ
ตอนนี้มีวิธีแสดงสแต็กการโทรด้วยฟังก์ชั่นการประมวลผลทั่วโลก เราจะทำอย่างไรกับมัน? ท้ายที่สุดเราต้องใช้สแต็คการโทร
บันทึกสแต็กการโทร
สแต็กการโทรมีประโยชน์มากในการจัดการข้อบกพร่อง ข่าวดีก็คือเบราว์เซอร์ให้ข้อมูลนี้ แม้ว่าแอตทริบิวต์สแต็กของวัตถุข้อผิดพลาดจะไม่ได้มาตรฐานในปัจจุบัน แต่โดยทั่วไปแล้วแอตทริบิวต์นี้รองรับในเบราว์เซอร์ใหม่
ดังนั้นสิ่งที่ยอดเยี่ยมที่เราทำได้คือพิมพ์ออกไปยังเซิร์ฟเวอร์:
window.addeVentListener ('ข้อผิดพลาด', ฟังก์ชั่น (e) {var stack = e.error.stack; var message = e.error.toString (); ถ้า (สแต็ก) {ข้อความ + = '/n' + สแต็ก;} var xhr = ใหม่ xmlhttprequest ();อาจไม่ชัดเจนในตัวอย่างรหัส แต่ตัวจัดการเหตุการณ์นี้จะถูกเรียกใช้โดยรหัสข้อผิดพลาดก่อนหน้า ดังที่ได้กล่าวไว้ข้างต้นตัวจัดการแต่ละตัวมีจุดประสงค์เดียวซึ่งทำให้รหัสแห้ง (อย่าทำซ้ำตัวเองโดยไม่ต้องทำล้อซ้ำ ๆ ) สิ่งที่ฉันสนใจคือวิธีการจับข้อความเหล่านี้บนเซิร์ฟเวอร์
นี่คือภาพหน้าจอของ Node Runtime:
สแต็คการโทรมีประโยชน์มากสำหรับการดีบักรหัส อย่าประมาทบทบาทของสแต็คการโทร
การประมวลผลแบบอะซิงโครนัส
โอ้มันค่อนข้างอันตรายที่จะจัดการรหัสแบบอะซิงโครนัส! JavaScript นำรหัสแบบอะซิงโครนัสออกจากสภาพแวดล้อมการดำเนินการปัจจุบัน ซึ่งหมายความว่ามีปัญหากับคำสั่งลอง ... จับคำสั่งด้านล่าง
ฟังก์ชั่น asynchandler (fn) {ลอง {settimeout (function () {fn ();}, 1); } catch (e) {}}ยังมีส่วนที่เหลืออยู่ของการทดสอบหน่วยนี้:
มัน ('ไม่จับข้อยกเว้นที่มีข้อผิดพลาด', function () {var fn = function () {โยน typeError ใหม่ ('ประเภทข้อผิดพลาด');}; faileDpromise (ฟังก์ชั่น () {เป้าหมาย (fn);}) -ฉันต้องยุติตัวจัดการนี้ด้วยสัญญาที่จะตรวจสอบข้อยกเว้น โปรดทราบว่าถึงแม้ว่ารหัสของฉันจะลอง ... จับได้ แต่ข้อยกเว้นที่ไม่สามารถจัดการยังคงปรากฏขึ้นได้ ใช่ลอง ... จับได้เฉพาะการทำงานในสภาพแวดล้อมการดำเนินการแยกต่างหาก เมื่อมีการโยนข้อยกเว้นสภาพแวดล้อมการดำเนินการของล่ามจะไม่ได้เป็นบล็อกลองจับในปัจจุบันอีกต่อไป พฤติกรรมนี้เกิดขึ้นคล้ายกับการโทร AJAX ดังนั้นตอนนี้มีสองตัวเลือก อีกทางเลือกหนึ่งคือการจับข้อยกเว้นในการโทรกลับแบบอะซิงโครนัส:
settimeout (function () {ลอง {fn ();} catch (e) {// จัดการข้อผิดพลาด async นี้}}, 1);แม้ว่าวิธีนี้จะมีประโยชน์ แต่ก็ยังมีพื้นที่มากมายสำหรับการปรับปรุง ก่อนอื่นลอง ... บล็อกจับรหัสจะปรากฏขึ้นทุกที่ในรหัส ในความเป็นจริงในปี 1970 การเขียนโปรแกรมพวกเขาต้องการให้รหัสของพวกเขาถอยกลับ นอกจากนี้เครื่องยนต์ V8 กีดกันการใช้งานลอง ... บล็อกรหัสจับในฟังก์ชั่น (V8 เป็นเครื่องยนต์จาวาสคริปต์ที่ใช้โดยเบราว์เซอร์ Chrome และโหนด) พวกเขาแนะนำให้เขียนบล็อกของรหัสเหล่านี้ที่จับข้อยกเว้นที่ด้านบนของสแต็กการโทร
แล้วสิ่งนี้บอกอะไรเรา? ดังที่ฉันได้กล่าวไว้ข้างต้นตัวจัดการข้อผิดพลาดทั่วโลกในบริบทการดำเนินการใด ๆ เป็นสิ่งจำเป็น หากคุณเพิ่มตัวจัดการข้อผิดพลาดลงในวัตถุหน้าต่างนั่นคือคุณทำเสร็จแล้ว! การปฏิบัติตามหลักการของแห้งและแข็งหรือไม่? ตัวจัดการข้อผิดพลาดทั่วโลกจะทำให้รหัสของคุณอ่านได้และสะอาด
ต่อไปนี้เป็นรายงานที่พิมพ์บนการจัดการข้อยกเว้นฝั่งเซิร์ฟเวอร์ โปรดทราบว่าหากคุณใช้รหัสในตัวอย่างผลลัพธ์อาจแตกต่างกันเล็กน้อยขึ้นอยู่กับเบราว์เซอร์ที่คุณใช้
ตัวจัดการนี้สามารถบอกฉันได้ว่าข้อผิดพลาดมาจากรหัสแบบอะซิงโครนัส มันบอกฉันว่าข้อผิดพลาดมาจากตัวจัดการ settimeout () เจ๋งมาก!
ข้อผิดพลาดเป็นส่วนหนึ่งของทุกแอปพลิเคชัน แต่การจัดการข้อผิดพลาดที่เหมาะสมไม่ใช่ มีอย่างน้อยสองวิธีในการจัดการกับข้อผิดพลาด หนึ่งคือทางออกของความล้มเหลวความเงียบนั่นคือละเว้นข้อผิดพลาดในรหัส อีกวิธีหนึ่งคือการตรวจจับและแก้ไขข้อผิดพลาดอย่างรวดเร็วเช่นหยุดที่ข้อผิดพลาดและทำซ้ำ ฉันคิดว่าฉันได้แสดงอย่างชัดเจนว่าฉันชอบอะไรและทำไมฉันถึงชอบ ทางเลือกของฉัน: อย่าซ่อนปัญหา ไม่มีใครจะตำหนิคุณสำหรับเหตุการณ์ที่ไม่คาดคิดในโปรแกรมของคุณ สิ่งนี้เป็นที่ยอมรับเพื่อทำลายคะแนนทำซ้ำและลองใช้ผู้ใช้ ในโลกที่ไม่สมบูรณ์มันเป็นสิ่งสำคัญที่จะให้โอกาสตัวเอง ข้อผิดพลาดหลีกเลี่ยงไม่ได้และสิ่งที่คุณทำเป็นสิ่งสำคัญในการแก้ไขข้อผิดพลาด การใช้คุณสมบัติการจัดการข้อผิดพลาดของ JavaScript อย่างสมเหตุสมผลและการถอดรหัสแบบอัตโนมัติและยืดหยุ่นสามารถทำให้ประสบการณ์ของผู้ใช้ราบรื่นขึ้นและทำให้การวินิจฉัยของนักพัฒนาทำงานง่ายขึ้น