การลบข้อผิดพลาดในการจับสัญญา


93

ฉันพบรหัสต่อไปนี้ในบทช่วยสอน:

promise.then(function(result){
    //some code
}).catch(function(error) {
    throw(error);
});

ฉันสับสนเล็กน้อย: การโทรจับได้สำเร็จหรือไม่? สำหรับฉันแล้วดูเหมือนว่ามันจะไม่มีผลใด ๆ เนื่องจากมันเพียงแค่แสดงข้อผิดพลาดเดียวกันกับที่ถูกจับได้ ฉันยึดตามวิธีการทำงานของ try / catch เป็นประจำ


คุณช่วยให้ลิงค์ไปยังบทช่วยสอนได้หรือไม่? อาจมีบริบทเพิ่มเติมที่จะเป็นประโยชน์ ...
อิกอร์

@Igor ฉันทำไม่ได้มันอยู่ใน Pluralsight นี่อาจเป็นเพียงตัวยึดสำหรับตรรกะในการส่งข้อผิดพลาดหรือไม่?
Tyler Durden

นั่นคือสิ่งที่ฉันเดาได้เพราะมันไม่ได้ทำอะไรอีกแล้วส่งข้อผิดพลาดไปยังผู้โทรซึ่งสามารถทำได้โดยไม่ต้องมีจุดเริ่มต้นด้วย
Igor

1
@TylerDurden ฉันสงสัยว่าคุณถูกต้องเกี่ยวกับการเป็นตัวยึดตำแหน่ง
Jared Smith

@TylerDurden ฉันเดาด้วยว่ามันเป็นตัวยึด อาจจะพยายามสาธิตวิธีการจัดรูปแบบ / ทำให้ปกติเกิดข้อผิดพลาด try { ... }catch(error){ throw new Error("something went wrong") }โดยทั่วไปสัญญาเทียบเท่าการ หรือแสดงให้เห็นว่าสัญญาและข้อผิดพลาดที่เข้ากันได้(อย่างน้อยที่ทางรอบ) แต่ในการใช้งานปัจจุบันมันโง่มาก คุณพูดถูกมันไม่ได้ทำอะไรเลยและมันก็ไม่เหมือนกับตะขอที่คุณเพิ่มใน OOP เพื่อเปิดใช้งานการเขียนทับในคลาสที่สืบทอด ฉันจะเพิ่ม catch-block ทันทีที่ทำอะไรบางอย่าง แต่ไม่ใช่อย่างนั้นไม่ใช่แค่เป็นตัวยึด
Thomas

คำตอบ:


130

ไม่มีจุดที่จะจับเปลือยและโยนอย่างที่คุณแสดง มันไม่ได้ทำประโยชน์อะไรเลยนอกจากเพิ่มโค้ดและการดำเนินการช้า ดังนั้นหากคุณกำลังจะ.catch()ปลูกใหม่ควรมีบางสิ่งที่คุณต้องการทำใน.catch()มิฉะนั้นคุณควรลบ.catch()ทั้งหมด

จุดปกติสำหรับโครงสร้างทั่วไปนั้นคือเมื่อคุณต้องการดำเนินการบางอย่าง.catch()เช่นบันทึกข้อผิดพลาดหรือล้างสถานะบางอย่าง (เช่นไฟล์ปิด) แต่คุณต้องการให้ห่วงโซ่สัญญาดำเนินการต่อเมื่อถูกปฏิเสธ

promise.then(function(result){
    //some code
}).catch(function(error) {
    // log and rethrow 
    console.log(error);
    throw error;
});

ในบทช่วยสอนอาจมีไว้เพื่อแสดงให้ผู้คนเห็นว่าพวกเขาสามารถจับข้อผิดพลาดได้ที่ไหนหรือสอนแนวคิดในการจัดการข้อผิดพลาดจากนั้นจึงเปลี่ยนใหม่


เหตุผลที่เป็นประโยชน์บางประการในการจับและปลูกใหม่มีดังนี้:

  1. คุณต้องการบันทึกข้อผิดพลาดแต่ให้ห่วงโซ่สัญญาว่าถูกปฏิเสธ
  2. คุณต้องการเปลี่ยนข้อผิดพลาดให้เป็นข้อผิดพลาดอื่น ๆ (โดยมากเพื่อให้การประมวลผลข้อผิดพลาดง่ายขึ้นที่ส่วนท้ายของห่วงโซ่) ในกรณีนี้คุณจะต้องแก้ไขข้อผิดพลาดอื่น
  3. คุณต้องการประมวลผลจำนวนมากก่อนที่ห่วงโซ่สัญญาจะดำเนินต่อไป (เช่นทรัพยากรที่ปิด / ไม่มีค่าใช้จ่าย) แต่คุณต้องการให้ห่วงโซ่สัญญาถูกปฏิเสธ
  4. คุณต้องการจุดที่จะวางเบรกพอยต์สำหรับดีบักเกอร์ณ จุดนี้ในห่วงโซ่สัญญาหากเกิดความล้มเหลว

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


จากความเห็นของฉันมันไม่ใช่ตัวอย่างที่ดี ด้วยวิธีการดังกล่าวคุณจะได้รับการบันทึกหลายรายการสำหรับ 1 ข้อผิดพลาด ใน java คุณสามารถthrow new Exception(periousException);ฉันไม่รู้ว่า javascript สนับสนุนข้อผิดพลาดที่ซ้อนกันหรือไม่ แต่อย่างไรก็ตาม "log and throw" เป็นการปฏิบัติที่ไม่ดี
เชอร์รี่

27
@ เชอร์รี่ - คุณไม่สามารถพูดได้ว่านี่เป็นการปฏิบัติที่ไม่ดีโดยทั่วไป มีหลายครั้งที่โมดูลต้องการบันทึกข้อผิดพลาดในแบบของตัวเองและนี่เป็นวิธีหนึ่งในการทำเช่นนั้น นอกจากนี้ฉันไม่แนะนำสิ่งนี้ฉันแค่อธิบายว่าไม่มีเหตุผลที่จะมี.catch()และโยนข้อผิดพลาดเดียวกันในการจับเว้นแต่คุณจะทำอะไรบางอย่างในไฟล์.catch(). นั่นคือประเด็นของคำตอบนี้
jfriend00

โดยทั่วไปข้อยกเว้นควรพอดีกับระดับของนามธรรม มันตกลงอย่างสมบูรณ์ที่จะจับข้อยกเว้นที่เกี่ยวข้องกับฐานข้อมูลเช่นและโยนข้อยกเว้นบางอย่างเช่น "บริการ" ซึ่งจะถูกจัดการโดยผู้โทร สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณไม่เปิดเผยรายละเอียดเกี่ยวกับข้อยกเว้นระดับต่ำ
maxTrialfire

3
อีกเหตุผลที่ดีในการจับและ (บางครั้ง) โยนคือการจัดการกับข้อผิดพลาดที่เฉพาะเจาะจง แต่กลับทิ้งสิ่งอื่นทั้งหมด
Jasper

2
@SimonZyx - ใช่.finally()มีประโยชน์มากสำหรับสิ่งนั้น แต่บางครั้งทรัพยากรได้รับการดูแลแล้วในเส้นทางที่ไม่เกิดข้อผิดพลาดดังนั้นจึง.catch()ยังคงเป็นที่สำหรับปิด มันขึ้นอยู่กับสถานการณ์จริงๆ
jfriend00

16

ทั้งสอง.then()และ.catch()วิธีการคืนสัญญาและถ้าคุณโยนข้อยกเว้นในตัวจัดการใด ๆ สัญญาที่ส่งคืนจะถูกปฏิเสธและข้อยกเว้นจะถูกจับในตัวจัดการการปฏิเสธถัดไป

ในรหัสต่อไปนี้เรามีข้อยกเว้นในข้อแรก.catch()ซึ่งถูกจับได้ในข้อที่สอง.catch():

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
});

ครั้งที่สอง.catch()ส่งคืนสัญญาที่สำเร็จแล้ว.then()ผู้จัดการสามารถเรียกได้ว่า:

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
})
.then(() => {
    console.log('Show this message whatever happened before');
});

ข้อมูลอ้างอิงที่เป็นประโยชน์: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_after_a_catch

หวังว่านี่จะช่วยได้!


4

ไม่มีความแตกต่างที่สำคัญหากคุณละทิ้งcatchการเรียกใช้เมธอดอย่างสมบูรณ์

สิ่งเดียวที่เพิ่มคือ microtask พิเศษซึ่งในทางปฏิบัติหมายความว่าคุณจะสังเกตเห็นการปฏิเสธคำสัญญาในภายหลังมากกว่ากรณีของสัญญาที่ล้มเหลวโดยไม่มีcatchข้อ

ตัวอย่างถัดไปแสดงให้เห็นถึงสิ่งนี้:

var p;
// Case 1: with catch
p = Promise.reject('my error 1')
       .catch(function(error) {
          throw(error);
       });

p.catch( error => console.log(error) );
// Case 2: without catch
p = Promise.reject('my error 2');

p.catch( error => console.log(error) );

สังเกตวิธีรายงานการปฏิเสธครั้งที่สองก่อนครั้งแรก นั่นเป็นเรื่องของความแตกต่างเพียงอย่างเดียว


3

ดูเหมือนคำถามของคุณคือ "ในห่วงโซ่สัญญา.catch()วิธีการทำอะไร"

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw

คำสั่ง throw "จะหยุด (คำสั่งหลังการโยนจะไม่ถูกดำเนินการ) และการควบคุมจะถูกส่งต่อไปยังบล็อกแรกใน call stack หากไม่มีบล็อก catch ในฟังก์ชันผู้โทรโปรแกรมจะยุติ"

ในห่วงโซ่สัญญา.then()วิธีการจะส่งคืนข้อมูลบางประเภท การกลับมาของก้อนนี้จะทำให้คำสัญญาสมบูรณ์ การส่งคืนข้อมูลที่ประสบความสำเร็จเป็นไปตามสัญญา คุณสามารถคิด.catch()วิธีการในลักษณะเดียวกัน .catch()อย่างไรก็ตามจะจัดการกับการดึงข้อมูลที่ไม่สำเร็จ คำสั่งโยนสำเร็จตามสัญญา ในบางครั้งคุณจะเห็นนักพัฒนาใช้.catch((err) => {console.log(err))} ซึ่งจะทำให้ห่วงโซ่สัญญาสมบูรณ์


0

คุณไม่จำเป็นต้องโยนซ้ำเพียงแค่ปล่อย Promise.catch ว่างไว้มิฉะนั้นจะถือว่ายกเลิกการจัดการการปฏิเสธจากนั้นห่อรหัสในการลองจับและจะตรวจจับข้อผิดพลาดโดยอัตโนมัติซึ่งกำลังส่งผ่านไป

try{
  promise.then(function(result){
    //some code
  }).catch(function(error) {
    //no need for re throwing or any coding. but leave this as this otherwise it will consider as un handled
  });
}catch(e){
  console.log(e);
  //error can handle in here
}

0

ในห่วงโซ่สัญญาจะดีกว่าถ้าใช้ .catch

เช่นในฟังก์ชัน f2: .then (... ). จับ (e => ปฏิเสธ (e));

  • test1 - ด้วยการลองจับ
  • test2 - โดยไม่ต้องลองหรือ
  • test3 - ด้วย

function f1() {
    return new Promise((resolve, reject) => {
        throw new Error('test');
    });
}

function f2() {
    return new Promise((resolve, reject) => {
        f1().then(value => {
            console.log('f1 ok ???');
        }).catch(e => reject(e));
    });
}

function test1() {
    console.log('test1 - with try catch - look in F12');
    try {
      f2().then(() => { // Uncaught (in promise) Error: test
        console.log('???'); });
    } catch (e) {
      console.log('this error dont catched');
    }
}

function test2() {
    console.log('test2 - without try or .catch - look in F12');
    f2(); // Uncaught (in promise) Error: test
}

function test3() {
  console.log('test3 - with .catch');
  f2().then(value => {
    console.log('??');
  }).catch(e => {
    console.log(' now its ok, error ', e);
  })
}

setTimeout(() => { test1(); 
  setTimeout(() => { test2(); 
    setTimeout(() => { test3(); 
    }, 100);
  }, 100);
}, 100);

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