NodeJS UnhandledPromiseRejectionWarning
Итак, я тестирую компонент, который опирается на источник событий. Для этого я предложил решение, использующее Promises с Mocha + Chai:
it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
done();
}).catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
});
В консоли я получаю "UnhandledPromiseRejectionWarning", хотя вызывается функция отклонения, поскольку она мгновенно отображает сообщение "AssertionError: Ошибка обещания"
(узел: 25754) UnhandledPromiseRejectionWarning: необработанное обещание отклонение (id отклонения: 2): AssertionError: ошибка обещания: ожидается {Object (message, showDiff,...)} быть ложным 1) должен перейти с правильным событием
И затем, через 2 секунды, я получаю
Ошибка: превышено время ожидания 2000 мс. Убедитесь, что обратный вызов done() вызывается в этом тесте.
Что еще более странно, поскольку был выполнен обратный вызов catch (я думаю, что по какой-то причине ошибка assert помешала завершению выполнения)
Самое смешное, если я закомментирую assert.isNotOk(error...)
, тест будет работать без всякого предупреждения в консоли. Он по-прежнему "терпит неудачу" в том смысле, что выполняет задание.
Но все же я не могу понять эти ошибки с обещанием. Может ли кто-нибудь просветить меня?
Ответы
Ответ 1
Проблема вызвана следующим:
.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
Если утверждение не выполняется, оно выдает ошибку. Эта ошибка приведет к тому, что done()
никогда не будет вызван, потому что код был ошибочен перед этим. Это то, что вызывает таймаут.
"Отказ от необработанного обещания" также вызван неудавшимся утверждением, потому что если в обработчике catch()
возникает ошибка, и нет последующего обработчика catch()
, ошибка будет проглатываться (как объясняется в этой статье). Предупреждение UnhandledPromiseRejectionWarning
предупреждает вас об этом факте.
В общем, если вы хотите проверить код на основе обещаний в Mocha, вы должны полагаться на то, что сам Mocha может обрабатывать promises уже. Вы не должны использовать done()
, но вместо этого верните обещание из своего теста. Mocha затем поймает любые ошибки.
Вот так:
it('should transition with the correct event', () => {
...
return new Promise((resolve, reject) => {
...
}).then((state) => {
assert(state.action === 'DONE', 'should change state');
})
.catch((error) => {
assert.isNotOk(error,'Promise error');
});
});
Ответ 2
Я получил эту ошибку при заглушении с помощью sinon.
Исправление заключается в использовании пакета npm sinon-as-обещано при разрешении или отклонении обещаний с заглушками.
Вместо...
sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))
Используйте...
require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));
Существует также метод resolves (обратите внимание на конец).
См. Http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejected
Ответ 3
Библиотеки утверждений в Mocha работают, выдавая ошибку, если утверждение было неверным. Возникновение ошибки приводит к отклонению обещания, даже если оно было выдано в функции executor, предоставленной методу catch
.
.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
В приведенном выше коде возражаемая error
оценивается как true
поэтому библиотека утверждений выдает ошибку... которая никогда не перехватывается. В результате ошибки метод done
никогда не вызывается. Обратный вызов Mocha done
принимает эти ошибки, поэтому вы можете просто завершить все цепочки обещаний в Mocha с помощью .then(done,done)
. Это гарантирует, что метод done всегда вызывается, и ошибка будет сообщаться так же, как когда Mocha отлавливает ошибку подтверждения в синхронном коде.
it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then(((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
})).then(done,done);
});
Я воздаю должное этой статье за идею использования .then (сделано, сделано) при тестировании обещаний в Mocha.
Ответ 4
Для тех, кто ищет сообщение об ошибке/предупреждении UnhandledPromiseRejectionWarning
вне среды тестирования, возможно, это связано с тем, что никто в коде не заботится о возможной ошибке в обещании:
Например, этот код покажет предупреждение в этом вопросе:
new Promise((resolve, reject) => {
return reject('Error reason!');
});
(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!
и добавление .catch()
или обработка ошибки должны устранить предупреждение/ошибку
new Promise((resolve, reject) => {
return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });
Или используя второй параметр в функции then
new Promise((resolve, reject) => {
return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
Ответ 5
Если у вас есть async helperFunction()
, вызванный из вашего теста... (один из объяснений с ключевым словом ES7 async
, я имею в виду)
→ убедитесь, что вы называете это await helperFunction(whateverParams)
(ну да, естественно, как только вы знаете...)
И для этого для работы (чтобы избежать "ожидания - это зарезервированное слово), ваша тестовая функция должна иметь внешний асинхронный маркер:
it('my test', async () => { ...
Ответ 6
Я решил эту проблему после удаления веб-пакета (реагирует на проблему с js).
sudo uninstall webpack
Ответ 7
Я столкнулся с этой проблемой:
(node: 1131004) UnhandledPromiseRejectionWarning: отказ от необработанного обещания (re jection id: 1): TypeError: res.json не является функцией (node: 1131004) Отказ. Предупреждение. Отклонения от необработанного обещания устарели. В будущем обещание отклонения, которое не обрабатывается, приведет к завершению Node.j s с ненулевым кодом выхода.
Это была моя ошибка, я заменил res
объект в then(function(res)
, поэтому изменил res
на результат, и теперь он работает.
Wrong
module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(res){//issue was here, res overwrite
return res.json(res);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});
Коррекция
module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(result){//res replaced with result
return res.json(result);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});
Код службы:
function update(data){
var id = new require('mongodb').ObjectID(data._id);
userData = {
name:data.name,
email:data.email,
phone: data.phone
};
return collection.findAndModify(
{_id:id}, // query
[['_id','asc']], // sort order
{$set: userData}, // replacement
{ "new": true }
).then(function(doc) {
if(!doc)
throw new Error('Record not updated.');
return doc.value;
});
}
module.exports = {
update:update
}