Должен ли я воздерживаться от асинхронной обработки отклонения 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);
});