ลองจับจาวาสคริปต์ ... ไม่ใช่วิธีที่ดีใช่มั้ย


69

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


2
While in java or *any other language* it is mandatory to have error handling...- ไม่จริง Java ใช่ แต่มีภาษามากมายที่ไม่ยืนยันในการลองจับ (เช่น C #)
Jim G.

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

ฉันจะพนันว่าคุณจะเห็นการลองจับในโค้ดฝั่งเซิร์ฟเวอร์มากกว่าโค้ดฝั่งไคลเอ็นต์ โค้ดด้าน cliend ส่วนใหญ่ที่คุณเป็นส่วนตัวนั้นไม่ได้ทำอะไรสำคัญเลย ฉันอยากจะลด KB ลงไปที่ pip นั้นสำคัญกว่าการกู้คืนจากทุกข้อผิดพลาด - ในแอปพลิเคชันส่วนใหญ่และสถานการณ์ด้านเบราว์เซอร์
svidgen

มีวิธีหนึ่งในการหลีกเลี่ยงการลองจับโดยใช้บางทีและทั้งสอง (monads) เรามีเว็บ scrapper เขียนในโหนด เราสามารถลบลองทั้งหมดที่ใช้อยู่
user93

คำตอบ:


66

หนึ่งควรหลีกเลี่ยงthrowข้อผิดพลาดเป็นวิธีการผ่านเงื่อนไขข้อผิดพลาดในการใช้งาน

throwคำสั่งควรใช้เฉพาะ "สำหรับเรื่องนี้ไม่ควรเกิดขึ้นความผิดพลาดและการเผาไหม้. อย่ากู้คืนอย่างหรูหราในทางใดทางหนึ่ง"

try catch อย่างไรก็ตามถูกใช้ในสถานการณ์ที่วัตถุโฮสต์หรือ ECMAScript อาจทำให้เกิดข้อผิดพลาด

ตัวอย่าง:

var json
try {
    json = JSON.parse(input)
} catch (e) {
    // invalid json input, set to null
    json = null
}

คำแนะนำในชุมชนnode.jsคือคุณผ่านข้อผิดพลาดในการเรียกกลับ (เนื่องจากข้อผิดพลาดเกิดขึ้นสำหรับการดำเนินการแบบอะซิงโครนัสเท่านั้น) เป็นอาร์กิวเมนต์แรก

fs.readFile(uri, function (err, fileData) {
    if (err) {
        // handle
        // A. give the error to someone else
        return callback(err)
        // B. recover logic
        return recoverElegantly(err)
        // C. Crash and burn
        throw err
    }
    // success case, handle nicely
})

นอกจากนี้ยังมีปัญหาอื่น ๆ เช่นลอง / จับมีราคาแพงมากและมันก็น่าเกลียดและมันก็ไม่ได้ทำงานกับการทำงานแบบอะซิงโครนัส

ดังนั้นเนื่องจากการดำเนินการแบบซิงโครนัสไม่ควรเกิดข้อผิดพลาดและไม่สามารถทำงานกับการดำเนินการแบบอะซิงโครนัสจึงไม่มีใครลองใช้ catch catch ยกเว้นข้อผิดพลาดที่เกิดจากวัตถุโฮสต์หรือ ECMAScript


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

7
@zzzzBov ไม่มีอะไรผิดปกติกับการโยนข้อผิดพลาดสำหรับเคส C เกิดข้อผิดพลาดและเบิร์น ฉันไม่คิดว่าคุณควรตรวจจับข้อผิดพลาดและกู้คืน ยกตัวอย่างเช่นไม่เคยโยนเมื่อองค์ประกอบไม่อยู่มันก็จะส่งกลับdocument.getElementById nullเช่นเดียวกันสามารถทำได้เกือบทุกกรณี
Raynos

4
@ Raynos การขว้างปาจากฟังก์ชั่นการซิงค์นั้นเป็นที่ยอมรับอย่างสมบูรณ์และสมเหตุสมผลในกรณีที่ใช้ การส่งคืนข้อผิดพลาดในการติดต่อกลับเป็น async เนื่องจากข้อผิดพลาดในการส่งคือการซิงค์
sbartell

@Raynos มีคำแนะนำที่ดีเกี่ยวกับการหลีกเลี่ยงการใช้ข้อผิดพลาด / ข้อยกเว้นสำหรับการควบคุมการไหลบนไคลเอนต์ (สภาพแวดล้อมที่ไม่ใช่โหนด)? ฉันได้พบโพสต์นี้ใน StackOverflowแต่ส่วนใหญ่จะมุ่งไปที่ Java
blong

@ b.long ง่าย อย่าโยนข้อผิดพลาด แก้ไขปัญหา.
Raynos

32

ลอง / จับใน Javascript ไม่ได้เป็นสัญลักษณ์แสดงหัวข้อย่อยเหมือนในภาษาอื่นเนื่องจากลักษณะอะซิงโครนัสของ Javascript ลองพิจารณาตัวอย่างนี้:

try {
    setTimeout(function() {
        do_something_that_throws();
    }, 1000);
}
catch (e) {
    alert("You won't see this!");
}

ปัญหาคือว่าการควบคุมการไหลออกจากtryบล็อกก่อนที่จะdo_something_that_throws()ได้รับการดำเนินการดังนั้นข้อผิดพลาดที่เกิดขึ้นในการโทรกลับไม่เคยได้รับการจับ

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

setTimeout(function () {
    do_something_that_calls_err(function(err) {
        alert("Something went wrong, namely this: " + err);
    }),
    1000);

5
มันไม่ได้เป็นหลักฐานแสดงหัวข้อย่อยในภาษาอื่นเช่นกัน พอร์ตรหัสนั้นเป็นภาษาใด ๆ ที่รองรับการเรียกกลับแบบอะซิงโครนัสและจะล้มเหลวด้วย
Raynos

2
@ Raynos: คุณพูดถูก อย่างไรก็ตามภาษาอื่น ๆ (หรือมากกว่านั้นวัฒนธรรมที่สนับสนุนของพวกเขา) ไม่ซื้อสิ่งนี้อย่างมากในสำนวนแบบอะซิงโครนัส - โทรกลับอย่างที่ Javascript ทำและเนื่องจากลักษณะแบบมัลติเธรดของสภาพแวดล้อมอื่น ๆ ส่วนใหญ่ข้อบกพร่องของลอง / จับ รายล้อมไปด้วยคำเตือนอื่น ๆ มากมายดังนั้นผู้คนจึงเหยียบอย่างระมัดระวังในสถานการณ์การโทรกลับแบบมัลติเธรด / แบบอะซิงโครนัสและตระหนักถึงข้อผิดพลาดที่อาจเกิดขึ้น
tdammers

นี่เป็นเพราะธรรมชาติของ "อะซิงโครนัสธรรมชาติหรือไม่" เท่าที่ฉันสามารถเห็นได้ลอง / จับได้ในทางทฤษฎีอาจถูกสร้างขึ้นมาเพื่อทำงานแบบศัพท์เหมือนกับว่าตัวบ่งชี้ถูกปิดด้วยคำศัพท์อย่างไร มันไม่ได้เกิดขึ้นกับวิธีการทำงานใน JavaScript
Brian Gordon เมื่อ

2
ใน Node.js> = 0.8 มันเป็นมูลค่า noting มันเป็นไปได้ที่จะลอง / จับ asynchronously โปรดดูคำถามของฉันstackoverflow.com/questions/14301839/...
เบนจามิน Gruenbaum

ทางเลือกเดียวในการลองจับแบบซิงโครนัสคือรูปแบบการส่งต่อแบบอะซิงโครนัสหรือไม่
CMCDragonkai

23

จำนวนมากของคำตอบเหล่านี้เป็นบิตเก่าและไม่คำนึงถึงคุณสมบัติ ES7 ใหม่และasyncawait

การใช้async/ awaitคุณสามารถรับโฟลว์การควบคุมแบบอะซิงโครนัสได้ตามที่คุณต้องการ:

async function email(address) {
  try {
    // Do something asynchronous that may throw...
    await sendEmail({ to: address, from: 'noreply@domain.com`, subject: 'Hello' })
  } catch(err) {
    if (err instanceof SomeCustomError) {
      elegantlyHandleError(err)
    } else {
      throw err
    } 
  }
})

เรียนรู้เพิ่มเติมเกี่ยวกับasync/awaitที่นี่ คุณสามารถใช้async/ awaitตอนนี้ใช้Babel


แต่มันก็คล้ายกับรหัสซิงโครนัส
Atul Agrawal

@AtulAgrawal ใช่นั่นคือประเด็น Async / await ช่วยให้คุณสามารถเขียนรหัส async ในสไตล์ซิงโครนัสเพื่อให้คุณสามารถหลีกเลี่ยง "callback hell" และผูกมัดสัญญาไว้ด้วยกัน เป็นวิธีที่สนุกมากในการเขียนโค้ด async ในความคิดของฉัน
Dana Woodman

ดังนั้นสิ่งที่เป็นประโยชน์ของการใช้จาวาสคริปต์ถ้าเราทำรหัส async เพื่อซิงโครนัส เพราะคนส่วนใหญ่ใช้จาวาสคริปต์เนื่องจากลักษณะ async ของมัน
Atul Agrawal

2
@AtulAgrawal คุณจะได้รับสิ่งที่ดีที่สุดของทั้งสองโลก - คุณจะเพลิดเพลินไปกับธรรมชาติของภาษา async (เนื่องจากโค้ดข้างต้นจะยังคงถูกเรียกใช้งาน async โดยจะช่วยลดเธรดและอื่น ๆ ) และทำให้คุณได้รับประโยชน์จากสไตล์การเขียนโปรแกรม มันไม่มีปัญหาแม้ว่า ...
ZenMaster

15

try-catch ใน javascript นั้นถูกต้องและมีประโยชน์เช่นเดียวกับในภาษาอื่นที่ใช้งานได้ มีเหตุผลสำคัญอย่างหนึ่งที่ไม่ได้ใช้ใน javascript มากเท่าภาษาอื่น มันเป็นเหตุผลเดียวกันกับจาวาสคริปต์ที่ถูกมองว่าเป็นภาษาสคริปต์ที่น่าเกลียด แต่ก็เป็นเหตุผลเดียวกับที่คนทั่วไปคิดว่าโปรแกรมเมอร์จาวาสคริปต์ไม่ใช่โปรแกรมเมอร์ตัวจริง:

  • Javascript เป็นภาษาที่เข้าถึงได้ง่ายและแพร่หลาย

ข้อเท็จจริงที่ว่ามีผู้คนมากมายที่สัมผัสกับจาวาสคริปต์ (โดยอาศัยเพียงภาษาที่รองรับโดยเบราว์เซอร์) หมายความว่าคุณมีรหัสไม่เป็นมืออาชีพมากมาย แน่นอนว่ายังมีเหตุผลเล็กน้อยอีกหลายประการ:

  • บางสิ่งใน javascript เป็นแบบอะซิงโครนัสจึงไม่catchสามารถทำได้ (แบบอะซิงโครนัส)
  • มีการพูดคุยกันอย่างล้นหลามเกี่ยวกับความพยายามในการจับการแสดงที่ยิ่งใหญ่ มันมีผลงานเล็กน้อยแต่สำหรับโค้ดส่วนใหญ่มันก็คุ้มค่า
  • javascript ถูกนำไปใช้ (น่าเสียดาย) ในลักษณะที่มักจะละเว้นข้อผิดพลาดอย่างเงียบ ๆ (เช่นเปลี่ยนสตริงเป็นตัวเลขโดยอัตโนมัติ)

ไม่ควรใช้try-catch แต่คุณควรเรียนรู้วิธีใช้อย่างถูกต้อง - เหมือนทุกอย่างในการเขียนโปรแกรม


3
และทำไมคุณไม่ลงคะแนนลงโอ้ผู้ลงคะแนนเสียงเงียบ?
user250878

ตกลง ฉันคิดว่าคำตอบที่ได้รับการยอมรับนั้นเป็นเรื่องจริง แต่มีเหตุผลที่ดีในการใช้ลองจับและแม้กระทั่งการขว้างนอกเหนือจากการจัดการกับวัตถุพื้นเมือง เมื่อเกิดข้อผิดพลาดแทนที่จะส่งผ่านไปยังการเรียกกลับมันจะหยุดการดำเนินการต่อไปและข้ามสแต็กไปยังบล็อกแรกมากกว่าที่จะจัดการได้ นี่เป็นข้อได้เปรียบที่ยอดเยี่ยมและเหมาะสมอย่างยิ่งสำหรับการทำงานแบบซิงโครนัสโดยเฉพาะอย่างยิ่งหากพวกมันเกี่ยวข้องกับการทำรังลึก ดูเหมือนว่าบ้าที่จะแนะนำข้อยกเว้นคือ "ไม่ดี" (ดีกว่าเหตุผลที่ชัดเจน) - จริง ๆ แล้วพวกเขาเป็นเครื่องมือที่มีประโยชน์มากที่มี "พลัง" ที่ไม่เหมือนใคร
Semicolon

2
คุณเคยใช้เวลาอ่านซอร์สโค้ดของ jQuery หรือไม่? แทบจะไม่มีการทดลองใด ๆ ยกเว้นในกรณีที่กล่าวถึงในคำตอบที่ได้รับการยอมรับ: เพื่อตรวจสอบคุณสมบัติและใช้วัตถุเนทีฟ / โฮสต์ที่ทำให้เกิดข้อผิดพลาด ในการโทรหารหัสที่ไม่ได้ใช้ try-catch มาก (เช่น jQuery) ดูเหมือนว่า 'ไม่เป็นมืออาชีพ' ดูเหมือนจะโง่ พูดตามตรงฉันคิดว่ามันเป็นโปรแกรมเมอร์จาวาสคริปต์ตัวใหม่ที่มาจากจาวาที่มักจะใช้ฟีเจอร์ภาษามากเกินไปเช่นลองจับ
Stijn de Witt

6

ฉันเชื่อว่าเหตุผลส่วนใหญ่ที่try..catchหาได้ยากใน JavaScript คือเนื่องจากภาษามีความอดทนสูงสำหรับข้อผิดพลาด สถานการณ์ส่วนใหญ่สามารถจัดการได้โดยใช้การตรวจสอบโค้ดค่าเริ่มต้นที่ดีและเหตุการณ์แบบอะซิงโครนัส ในบางกรณีเพียงใช้รูปแบบจะป้องกันปัญหา:

function Foo() {
    //this may or may not be called as a constructor!!
    //could accidentally overwrite properties on window
}

function Bar() {
    if (!(this instanceof Bar)) {
        return new Bar();
    }
    //this will only work on Bar objects, and wont impact window
}

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

function doFoo(arg) {
    if (arg.foo) {
        arg.foo();
    } else {
        Bar.prototype.foo.call(arg);
    }
}

ด้วยการเพิ่มasync/ awaitเพื่อภาษาtry..catchกำลังเป็นที่แพร่หลายมากขึ้น สัญญาว่าจะเป็นรูปแบบอะซิงโครนัสของtry..catchมันทำให้รู้สึกว่าควรคาดหวัง:

doSomething().then(
  doSomethingWithResult,
  doSomethingWithError
)

เป็นแทนที่จะเขียนเป็น:

try {
  const result = await doSomething()
  doSomethingWithResult(result)
} catch (e) {
  doSomethingWithError(e)
}

3

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

ดังนั้นเบราว์เซอร์รุ่นเก่าบางรุ่นจึงไม่รองรับ (อันที่จริงแล้วมันอาจทำให้เกิดข้อผิดพลาดในการแยกวิเคราะห์ / ไวยากรณ์ในเบราว์เซอร์รุ่นเก่าบางสิ่งที่ยากต่อการ "ตั้งโปรแกรมป้องกัน" มากกว่าข้อผิดพลาดประเภทอื่น ๆ ส่วนใหญ่)

ที่สำคัญกว่านั้นเนื่องจากไม่สามารถใช้งานได้ในตอนแรกฟังก์ชัน Javascript ในตัวที่เปิดตัวครั้งแรก (สิ่งที่เรียกว่า "ห้องสมุด" ฟังก์ชั่นในหลายภาษา) ไม่ได้ใช้ประโยชน์ (มันทำงานได้ไม่ดีนักในการ "จับ" ข้อผิดพลาดจาก someobject.somefunction () ถ้ามันไม่ "โยน" แต่ให้ส่งคืน "null" แทนเมื่อพบปัญหา)


อีกเหตุผลที่เป็นไปได้ก็คือกลไกลอง / จับไม่ได้ดูเหมือนว่าจำเป็นในตอนแรก (และดูเหมือนจะไม่เป็นประโยชน์ทั้งหมด) มันจำเป็นจริงๆเท่านั้นเมื่อมีการโทรซ้อนกันหลายระดับเป็นประจำ การส่งคืน ERRNO บางประเภทอาจใช้งานได้ดีสำหรับการโทรโดยตรง (แม้ว่าจะทำให้มีประโยชน์จริง ๆ ทุกครั้งที่มีให้บริการแนวทางปฏิบัติที่ดีที่สุดในภาษาส่วนใหญ่คือการใช้ทุกที่แทนที่จะใช้กับการโทรที่ซ้อนกันอย่างล้ำลึก) เนื่องจากเดิมคาดว่าจาวาสคริปต์ลอจิกจะเล็กและเรียบง่าย (หลังจากทั้งหมดมันเป็นเพียงส่วนเสริมของเว็บเพจ :-), การเรียกใช้ฟังก์ชันไม่คาดว่าจะซ้อนกันอย่างลึกล้ำและกลไกการลอง / จับไม่จำเป็น


3
ไม่ไม่ไม่ไม่อย่างนั้น เอ็นจิ้นที่เก่าไม่สำคัญอีกต่อไปเป็นเวลานานและชุมชนจาวาสคริปต์โดยทั่วไปนั้นสามารถวางสิ่งเก่า ๆ ได้อย่างรวดเร็วเมื่อได้รับการพิสูจน์ ฉันจะพนันได้เลยว่านักพัฒนาจาวาสคริปต์ส่วนใหญ่ตอนนี้เป็น post-IE6
Camilo Martin

0

ฉันเชื่อว่าพวกเขาไม่ได้ใช้สิ่งเหล่านี้มากนักเนื่องจากการโยนข้อยกเว้นในโค้ดด้านไคลเอนต์ Javascript ทำให้การดีบักหน้าเว็บทำได้ยากขึ้น

แทนที่จะโยนข้อยกเว้นโดยทั่วไปฉันต้องการแสดงกล่องแจ้งเตือน (เช่นalert("Error, invalid...");)

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

เขาจะโทรหาคุณโดยบอกว่า: "เฮ้, หน้า X ใช้งานไม่ได้!"แล้วมันก็ขึ้นอยู่กับคุณแล้วว่าจะพบสิ่งที่ผิดพลาดและในรหัส

โดยการใช้กล่องการแจ้งเตือนแทนที่จะเป็นไปได้มากขึ้นที่เขาจะโทรหาและพูดว่า: "เฮ้เมื่อฉันคลิกที่ปุ่ม A ของหน้า X มันจะแสดงกล่องที่บอกว่า ... "เชื่อฉันสิมันจะง่ายกว่ามากในการค้นหา ข้อผิดพลาด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.