Як зловити невпійманий виняток у Promise


77

Чи є спосіб глобально охопити всі винятки, включаючи винятки Promise. Приклад:

    window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
        alert("Error occured: " + errorMsg);//or any message
        return false;
    }

    var myClass = function(){

    }


    var pr = new Promise(function(resolve, react){
        var myInstance = new myClass();
        myInstance.undefinedFunction(); // this will throw Exception
        resolve(myInstance);
    });


    pr.then(function(result){
        console.log(result);
    });

    // i know right will be this:
    // pr.then(function(result){
    //     console.log(result);
    // }).catch(function(e){
    //     console.log(e);
    // });

Цей сценарій мовчки помре без помилок. У фаєрбазі нічого.

Моє питання полягає в тому, що якщо я помиляюся і забув її зловити, чи є спосіб зловити її глобально?

Відповіді:


32

Оновлення, рідні обіцянки тепер роблять у більшості браузерів наступне:

window.addEventListener("unhandledrejection", function(promiseRejectionEvent) { 
    // handle error here, for example log   
});

Ми просто обговорювали це днями.

Ось як ви можете це зробити з bluebird:

window.onpossiblyunhandledexception = function(){
    window.onerror.apply(this, arguments); // call
}

window.onerror = function(err){
    console.log(err); // logs all errors
}

З Bluebird це також можна використовувати Promise.onPossiblyUnhandledRejection. Виклики doneне потрібні, оскільки бібліотека сама виявляє необроблене відхилення, на відміну від Q (UPDATE 2016 - я зараз написав код для Q, і він це робить).

Що стосується рідних обіцянок - вони будуть в кінцевому підсумку звіт або window.onerror або новий обробник , але процес специфікації ще не зроблено - ви можете слідувати його тут .


> у більшості браузерів: За даними MDN , єдиним браузером, який підтримує це станом на червень 2018 року, є Chrome.
Джон Хаттон,

1
@JohnHatton згідно StatCounter більшість браузерів Chrome. Тим не менш, ви завжди можете використовувати бібліотеку обіцянок, щоб отримати таку поведінку. Я перевірив людей у ​​Firefox (який має його під прапором) Safari та Edge, щоб перевірити, чи зможемо ми це зробити в інших браузерах найближчим часом.
Бенджамін Груенбаум,

1
"більшість браузерів - це Chrome" Гаразд, гадаю, "браузер, який використовується в більшості сеансів" - це одне з визначень "більшості браузерів" ... не те, яке я вибрав би.
Джон Хаттон,

15

В даний час більшість реалізацій обіцянок не надають тип функціональних можливостей, на які ви посилаєтесь, однак ряд сторонніх бібліотек обіцянок (включаючи Q та bluebird ) надають done()метод, який виявляє та відновлює будь-які непомічені помилки, виводячи їх на консоль .

( Редагувати: див. Відповідь Бенджаміна Груенбаума про особливості Bluebird для обробки невпійманих винятків)

Отже, якщо у вас це є, вам просто потрібно дотримуватися емпіричного правила, згідно з яким будь-яка обіцянка, яку ви використовуєте, повинна бути повернена або припинена за допомогою .done() :

pr.then(function(result){
    console.log(result);
})
.done();

Щоб цитувати посилання на Q API:

Золотим правилом done проти then використання є: або return ваша обіцянка комусь іншому, або якщо ланцюжок закінчується вами, зателефонуйте done припинити її. Завершення з catch недостатньо, оскількиcatch обробник може сам викликати помилку.

Я усвідомлюю, що для цього все ще потрібно трохи додаткової роботи, і ви все одно можете забути це зробити, але це покращення порівняно з необхідністю .catch()та явною обробкою кожної помилки, особливо з причини, зазначеної вище.


2
Насправді зроблене нічим не відрізняється від простого виконання .catch(thrower)(або того самого, використовуючи ярлик, наприклад, додавання Promise.prototype.done = .... Тож для цього досить безглуздо, особливо у bluebird, де про невловиті помилки повідомляється зазвичай.
Esailija,

1
@Esailija Чи можете ви детальніше це сказати? Що було throwerб у цьому випадку?
JLRishe

1
@Esailija Ok, добре, це може бути непотрібним у Bluebird, але це все-таки найпростіший спосіб запобігти поглинанню помилок у деяких інших реалізаціях (AFAIK), тому я б не називав це безглуздим.
JLRishe

1
@JLRishe Я думаю, його суть полягає в тому, що люди не повинні використовувати реалізації обіцянок, які ковтають помилки (наприклад, jQuery, Q, $ q тощо), а також використовувати бібліотеки обіцянок, які дозволяють відслідковувати не оброблені відхилення, такі як Bluebird, When, RSVP тощо
Бенджамін Груенбаум

1
Я мав на увазі безглуздо як окрему концепцію, крім прославленого кидка тайм-аут
Esailija

5

У Node.js ви можете використовувати:

process.on('unhandledRejection', (reason, promise) => {
  console.error(`Uncaught error in`, promise);
});

Це корисно при налагодженні в невідомому, але якщо ви знаєте, що обіцяє і маєте доступ, вам слід скористатися .catchабо .doneдля кращого ремонтопридатності
tsuz

У новішій версії Node це фактично стало стандартною поведінкою. Отже, помилки в невпійманих обіцянках будуть реєструватися навіть без додавання цього фрагмента коду.
JussiR

Як ти міг отримати обіцянку, яка спричинила це? чи файл функції? Річ у тім, що reason.stack завжди невизначений.
Loebre

Використання обіцянки (або setTimeout) створює новий стек, тому ви не можете побачити, звідки було викликано обіцянку. Хоча новіша версія Chrome (V8?) Дозволяє бачити попередні стеки, а також дуже допомагає в налагодженні асинхронного коду.
JussiR

2

Якщо ви пишете код, який може бути виконаний як у браузері, так і в середовищі Node.js, ви можете обернути свій код у функції самовиконання, наприклад:

var isNode = (typeof process !== 'undefined' && typeof process.on !== 'undefined');
(function(on, unhandledRejection) {
    // PUT ANY CODE HERE
    on(unhandledRejection, function(error, promise) {
        console.error(`Uncaught error in`, promise);
    });
    // PUT ANY CODE HERE
})(
    isNode ? process.on.bind(process) : window.addEventListener.bind(window),
    isNode ? "unhandledRejection" : "unhandledrejection"
);

Що станеться, якщо ви використовуєте цей код:

  • Якби ви запустили його в середовищі Node.js, ваш обробник був би приєднаний до об'єкта процесу і виконувався, коли у вас є невпійманий виняток у обіцянці.

  • Якби ви запустили його в середовищі браузера, ваш обробник буде приєднаний до об'єкта вікна і буде виконаний, коли у вас є невпійманий виняток в обіцянці, а ваш браузер підтримує unhandledrejectionподію.

  • Якщо ви запустите його в середовищі браузера без підтримки для unhandledrejection, ви не зможете зловити свій невпійманий виняток, і ваш unhandledrejectionобробник подій ніколи не буде спрацьовувати, але ви не отримаєте жодних помилок, якщо немає незахоплених винятків.


1

Якщо ви використовуєте рідну Promise, це досить просто.
Вам потрібно лише .catchвідхилити деякі де.

ajax(request).catch(function(rejected){
      console.log(rejected);
});

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


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