Помилка відновлення в обіцянку обіцянки


88

У підручнику я знайшов такий код:

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

Я трохи заплутався: чи виклик catch щось робить? Мені здається, це не має жодного ефекту, оскільки просто викидає ту саму помилку, яку було виявлено. Я базую це на тому, як працює звичайна спроба / ловля.


Не могли б ви надати посилання на підручник? Можливо, є додатковий контекст, який був би корисним ...
Ігор

@Igor Я не можу, це на Pluralsight. Це можливо лише заповнювач для певної логіки передачі помилок?
Тайлер Дурден,

Це те, що я міг би здогадатися, оскільки він більше нічого не робить, а потім передає помилку разом із абонентом, що також може бути досягнуто, не маючи для початку лову
Ігор

1
@TylerDurden Я підозрюю, що ви правильно сказали, що це заповнювач.
Джаред Сміт,

@TylerDurden, я також здогадуюсь, що це заповнювач. Можливо, намагаюся продемонструвати, як форматувати / нормалізувати помилки. В основному обіцянка-еквівалент try { ... }catch(error){ throw new Error("something went wrong") }. Або показати, що обіцянки та помилки сумісні (принаймні так) . Але в його поточній реалізації це просто дурно. Ви маєте рацію, він нічого не робить і навіть не схожий на гачок, який ви б додали в ООП, щоб дозволити його перезапис у класі успадкування. Я б додав блок catch, як тільки він щось зробить, але не так, не просто як заповнювач.
Томас

Відповіді:


124

Немає сенсу голий улов і кидок, як ви показуєте. Він не робить нічого корисного, крім додавання коду та повільного виконання. Отже, якщо ви збираєтеся .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. Ви хочете, щоб на цьому етапі ланцюжка обіцянок було встановлено точку зупинки для налагоджувача, якщо виникла помилка.

Але звичайний вилов та повторне вилучення тієї самої помилки без жодного іншого коду в обробнику catch не робить нічого корисного для нормального запуску коду.


На мій погляд, це не добрий приклад. Завдяки такому підходу ви легко отримуєте кілька журналів за 1 помилку. У Java ви можете просто throw new Exception(periousException);я не знаю, чи підтримує javascript вкладену помилку, але в будь-якому випадку "реєструвати і кидати" - погана практика.
Cherry

26
@Cherry - Ви не можете сказати, що це взагалі погана практика. Бувають випадки, коли модуль хоче реєструвати власні помилки по-своєму, і це один із способів зробити це. Крім того, я не рекомендую цього, я просто пояснюю, що немає жодної причини мати .catch()та викидати ту саму помилку всередину catch, якщо ви не зробите ЩОЩО ще в .catch(). У цьому полягає суть цієї відповіді.
jfriend00

Як правило, винятки повинні відповідати рівню абстракції. Це цілком нормально, щоб, наприклад, схопити виняток, пов’язаний з db, і кинути щось на зразок винятку "сервіс", який буде обробляти абонент. Це особливо корисно, коли ви не можете
викласти

3
Ще однією вагомою причиною, щоб зловити і (іноді) кинути, є усунення певної помилки, але все інше відкинути.
Джаспер

2
@SimonZyx - Так, .finally()може бути для цього дуже корисним, але іноді про ресурси вже піклуються на шляху без помилок, тому .catch()все ще є місце для їх закриття. Це насправді залежить від ситуації.
jfriend00

15

І методи, .then()і .catch()методи повертають Promises, і якщо ви додасте виняток в будь-який обробник, повернута обіцянка буде відхилена, а виняток буде виявлено в наступному обробнику відхилення.

У наступному коді ми викидаємо виняток у першому .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()повертає Promised, що виконано, .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виклик методу.

Єдине, що він додає, це додаткова мікрозадача, що на практиці означає, що ви помітите відхилення обіцянки пізніше, ніж це стосується обіцянки, яка не виконується без 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 "зупиниться (оператори після кидання не будуть виконані), і керування буде передано першому блоку catch у стеці викликів. Якщо серед функцій, що викликають, не існує блоку catch, програма завершить роботу."

У ланцюжку обіцянок .then()метод поверне певний тип даних. Це повернення шматка завершить обіцянку. Успішне повернення даних завершує обіцянку. Ви можете думати про .catch()метод так само. .catch()однак буде обробляти невдалі отримання даних. Оператор throw завершує обіцянку. Іноді ви побачите, як розробники використовують .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: .тем (...). catch (e => відхилити (e));

  • test1 - за допомогою try catch
  • test2 - без спроби або .catch
  • test3 - за допомогою .catch

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);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.