NodeJS UnhandledPromiseRejectionWarning


134

Отже, я тестую компонент, який спирається на емітер подій. Для цього я придумав рішення, використовуючи Обіцянки з 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: Нерозроблене відхилення обіцянки (ідентифікатор відхилення: 2): AssertionError: Помилка обіцянки: очікується {Об'єкт (повідомлення, showDiff, ...)} неправдивий 1) повинен перейти з правильною подією

А потім, через 2 секунди, я отримую

Помилка: перевищено час очікування 2000 мс. Переконайтеся, що завершено () зворотний виклик у цьому тесті.

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

Тепер найсмішніша річ, якщо я прокоментую assert.isNotOk(error...)тест, він справляється з будь-яким попередженням на консолі. Це все ще "провалюється" в тому сенсі, що він виконує вилов.
Але все-таки я не можу зрозуміти ці помилки обіцяно. Може хтось мене просвітить?


Я думаю, у вас є один додатковий набір фіксаторів та парен на самому останньому рядку. Видаліть їх і повторіть спробу.
зменшення

4
Це так круто, нове попередження про відмову від відхилення знаходить помилки в реальному житті і економить час людям. Стільки виграшів тут. Без цього попередження ваші тести минули б без будь-яких пояснень.
Бенджамін Грюнбаум

Відповіді:


161

Проблема викликана цим:

.catch((error) => {
  assert.isNotOk(error,'Promise error');
  done();
});

Якщо твердження не вдасться, воно призведе до помилки. Ця помилка не призведе done()до виклику ніколи, оскільки код помилився перед цим. Ось що викликає таймаут.

«Невідоме відмова обіцянки» також викликано невдалим твердженням, тому що якщо помилка виникають в catch()обробнику, і не існує наступний catch()оброблювач , помилка буде отримати проковтування (як описано в цій статті ). UnhandledPromiseRejectionWarningПопередження попереджає вас цей факт.

Загалом, якщо ви хочете протестувати на базі обіцянок код в Mocha, вам слід покластися на той факт, що Mocha сам вже може впоратися з обіцянками. Ви не повинні використовувати 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');
  });
});

7
Для когось цікавого, це стосується і жасмину.
Нік Радфорд

@robertklep Не вдасться назвати будь-яку помилку, а не лише ту, яку ви очікуєте? Я думаю, що цей стиль не спрацює, якщо ви намагаєтесь стверджувати про невдачі.
TheCrazyProgrammer

1
@TheCrazyProgrammer catchобробник, ймовірно, повинен бути переданий як другий аргумент then. Однак я не зовсім впевнений, який був задум ОП, тому я залишив це таким, яким є.
Робертклеп

1
А також для всіх, хто цікавиться жасмином, використовуйте done.fail('msg')в цьому випадку.
Paweł

Чи можете ви навести повний приклад того, куди має йти головний код проти тверджень, тут не так зрозуміло ..... особливо якщо ви стверджуєте значення з оригінального виклику служби / обіцянки в нашому коді. Чи відповідає реальна обіцянка нашого коду в рамках цієї іншої "мокської обіцянки" ??
bjm88

10

Я отримав цю помилку під час заїдання синоном.

Виправлення полягає у використанні пакету npm, як було обіцяно, при вирішенні або відхиленні обіцянок за допомогою заглушок.

Замість ...

sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))

Використовувати ...

require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));

Існує також метод вирішення (зверніть увагу на s в кінці).

Дивіться http://clarkdave.net/2016/09/node-v6-6-and-asynchronically-handled-promise-rejections


1
Тепер Sinon включає в себе методи "вирішує" та "відхиляє" для заглушок версії 2. Див. Npmjs.com/package/sinon-as-promised . Я все ще поставив +1, але відповідь не знав.
Андрій

9

Бібліотеки тверджень у Mocha працюють, видаючи помилку, якщо твердження було неправильним. Введення помилки призводить до відхиленої обіцянки, навіть коли вона виконується у функції виконавця, наданої catchметоду.

.catch((error) => {
  assert.isNotOk(error,'Promise error');
  done();
});

У наведеному вище коді errorзаперечений оцінює trueтак, що бібліотека тверджень видає помилку ... яка ніколи не потрапляє. В результаті помилки doneметод ніколи не викликається. Зворотний doneвиклик Mocha приймає ці помилки, тому ви можете просто припинити всі ланцюжки обіцянок у Mocha .then(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.


6

Для тих, хто шукає помилку / попередження 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 */ });

1
Звичайно, але я думаю, що в реальному житті ми використовуємо не просто, new Promise((resolve, reject) => { return reject('Error reason!'); })а функцію, function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}тому внутрішню функцію нам не потрібно використовувати, .catch()але для успішного впорядкування помилок досить використовувати під час виклику цієї функції test().catch(e => console.log(e))або версії async / очікуванняtry { await test() } catch (e) { console.log(e) }
mikep

1

Я зіткнувся з цим питанням:

(вузол: 1131004) UnhandledPromiseRejectionWarning: Нерозподілене відхилення обіцянки (ідентифікатор rectionction: 1): TypeError: res.json не є функцією (вузол: 1131004) DeprecationWarning: Неопрацьовані відхилення обіцянки зняті. Надалі відхилення обіцянок, які не обробляються, припинять процес Node.j s ненульовим кодом виходу.

Це була моя помилка: я замінював resоб'єкт then(function(res), так змінив resрезультат і зараз він працює.

Неправильно

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
}

1

Ось мій досвід використання E7 async / очікування :

Якщо у вас є async helperFunction()тестовий дзвінок з вашого тесту ... ( asyncя маю на увазі один експліцит із ключовим словом ES7 )

→ переконайтеся, що ви називаєте це так await helperFunction(whateverParams) (ну так, звичайно, як тільки знаєте ...)

І щоб це працювало (щоб уникнути "очікувати зарезервоване слово"), ваша тестова функція повинна мати зовнішній маркер асинхронізації:

it('my test', async () => { ...

4
Не потрібно називати це як await helperFunction(...). asyncФункція повертає обіцянку. Ви можете просто обробити повернуту обіцянку, як ви робите у функції, не позначеній, asyncщо відбувається, щоб повернути обіцянку. Справа в тому, щоб впоратися з обіцянкою, періодом. Будь функція є asyncчи ні, значення не має. awaitє лише одним із безлічі способів виконання обіцянки.
Луї

1
Правда. Але тоді мені доведеться вкласти лінійку на ловлю ... або мої тести пройдуть як помилкові позитивні результати, і будь-які невдалі обіцянки пройдуть непоміченими (з точки зору результатів testrunner). Тож чекаю, як мені здається, менше ліній і зусиль. - У всякому разі, забуття чекати було, що спричинило це UnhandledPromiseRejectionWarningдля мене ... таким чином ця відповідь.
Френк Нокк

0

У мене був подібний досвід роботи з Chai-Webdriver для Selenium. Я додав awaitдо твердження, і це вирішило проблему:

Приклад з використанням Cucumberjs:

Then(/I see heading with the text of Tasks/, async function() {
    await chai.expect('h1').dom.to.contain.text('Tasks');
});

-7

Я вирішив цю проблему після видалення веб-пакета (реагуйте на проблему js).

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