Должен ли я воздерживаться от асинхронной обработки отклонения Promise?

Я только что установил Node v7.2.0 и узнал, что следующий код:

var prm = Promise.reject(new Error('fail'));

результаты в этом сообщении:;

(node:4786) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: fail
(node:4786) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Я понимаю причину этого, так как многие программисты, вероятно, испытали разочарование Error в конечном итоге быть поглощенным Promise, Однако тогда я сделал этот эксперимент:

var prm = Promise.reject(new Error('fail'));

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
},
0)

что приводит к:

(node:4860) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: fail
(node:4860) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:4860) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
fail

Я на основании PromiseRejectionHandledWarning Предположим, что обработка Promise Отклонение асинхронно является / может быть плохой вещью.

Но почему это?

2 ответа

Решение

"Должен ли я воздерживаться от асинхронной обработки отклонения Promise?"

Эти предупреждения служат важной цели, но чтобы увидеть, как все это работает, посмотрите эти примеры:

Попробуй это:

process.on('unhandledRejection', () => {});
process.on('rejectionHandled', () => {});

var prm = Promise.reject(new Error('fail'));

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);

Или это:

var prm = Promise.reject(new Error('fail'));
prm.catch(() => {});

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);

Или это:

var var caught = require('caught');
var prm = caught(Promise.reject(new Error('fail')));

setTimeout(() => {
    prm.catch((err) => {
        console.log(err.message);
    })
}, 0);

Отказ от ответственности: я являюсь автором пойманного модуля (и да, я написал его для этого ответа).

обоснование

Он был добавлен в Node как одно из переломных изменений между v6 и v7. Об этом шла горячая дискуссия в выпуске № 830: Поведение по умолчанию при обнаружении необработанного отклонения без универсального соглашения о том, как должны вести себя обещания с обработчиками отклонения, присоединенными асинхронно - работать без предупреждений, работать с предупреждениями или вообще быть запрещено использовать при завершении программы, Больше обсуждения имело место в нескольких проблемах проекта unhandled-rejection-spec.

Это предупреждение поможет вам найти ситуации, когда вы забыли обработать отказ, но иногда вы можете избежать этого. Например, вы можете сделать несколько запросов и сохранить полученные обещания в массиве, чтобы потом обработать их в какой-то другой части вашей программы.

Одним из преимуществ обещаний по сравнению с обратными вызовами является то, что вы можете отделить место, где вы создаете обещание, от места (или мест), где вы прикрепляете обработчики. Эти предупреждения усложняют выполнение, но вы можете либо обработать события (мой первый пример), либо прикрепить фиктивный обработчик перехвата, где бы вы ни создавали обещание, которое вы не хотите обрабатывать сразу (второй пример). Или вы можете попросить модуль сделать это за вас (третий пример).

Как избежать предупреждений

Присоединение пустого обработчика никак не повлияет на работу сохраненного обещания, если вы сделаете это в два этапа:

var prm1 = Promise.reject(new Error('fail'));
prm1.catch(() => {});

Это не будет то же самое, хотя:

var prm2 = Promise.reject(new Error('fail')).catch(() => {});

Вот prm2 будет другое обещание тогда prm1, В то время как prm1 будет отклонено с ошибкой 'fail', prm2 будет решен с undefined что, вероятно, не то, что вы хотите.

Но вы могли бы написать простую функцию, чтобы она работала, как в примере с двумя шагами выше, как я сделал с caught модуль:

var prm3 = caught(Promise.reject(new Error('fail')));

Вот prm3 такой же как prm1,

Смотрите: https://www.npmjs.com/package/caught

Обновление 2017

См. Также запрос на извлечение # 6375: lib, src: "throw" для необработанных отклонений обещаний (еще не объединены по состоянию на февраль 2017 года), помеченных как Milestone 8.0.0:

Обещания "отбрасывают" отказы, которые выходят как обычные невосполнимые ошибки[выделение добавлено]

Это означает, что мы можем ожидать, что узел 8.x изменит предупреждение о том, что этот вопрос превращается в ошибку, которая приводит к сбою и завершает процесс, и мы должны учитывать это при написании наших программ сегодня, чтобы избежать неожиданностей в будущем.

Смотрите также Node.js 8.0.0 Tracking Issue # 10117.

Я предполагаю, что обработка отклонения Promise асинхронно - это плохо.

Да, это действительно так.

Ожидается, что вы все равно хотите немедленно обработать любой отказ. Если вы не справитесь (и, возможно, не справитесь), вы получите предупреждение.
Я почти не сталкивался с ситуациями, когда вы не хотели бы потерпеть неудачу сразу после получения отказа. И даже если вам нужно ждать чего-то еще после сбоя, вы должны сделать это явно.

Я никогда не видел случая, когда было бы невозможно сразу установить обработчик ошибок (попробуйте убедить меня в обратном). В вашем случае, если вы хотите слегка задержать сообщение об ошибке, просто сделайте

var prm = Promise.reject(new Error('fail'));

prm.catch((err) => {
    setTimeout(() => {
        console.log(err.message);
    }, 0);
});
Другие вопросы по тегам