NodeJS UnhandledPromiseRejectionWarning


134

Yani, olay yayıcıya dayanan bir bileşeni test ediyorum. Bunu yapmak için Mocha + Chai ile Promises'i kullanarak bir çözüm buldum:

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();
  });
});

Reddetme işlevi anında 'AssertionError: Promise error' mesajını gösterdiği için çağrılmasına rağmen konsolda bir 'UnhandledPromiseRejectionWarning' alıyorum

(düğüm: 25754) UnhandledPromiseRejectionWarning: İşlenmemiş söz reddi (ret id: 2): AssertionError: Promise error: beklenen {Object (message, showDiff, ...)} hatalı 1) doğru olayla geçiş yapmalı

Ve sonra, 2 saniye sonra

Hata: 2000 ms'lik zaman aşımı aşıldı. Bu testte done () geri aramasının çağrıldığından emin olun.

Bu, yakalama geri çağrısı yürütüldüğünden beri daha da tuhaf. (Sanırım bazı nedenlerden dolayı iddia hatası yürütmenin geri kalanını engelledi)

Şimdi komik olan şey, eğer yorum yaparsam assert.isNotOk(error...), konsolda herhangi bir uyarı olmadan test iyi çalışır. Yakalama işlemini gerçekleştirmesi anlamında hala "başarısız" oluyor.
Ama yine de bu hataları sözle anlayamıyorum. Biri beni aydınlatabilir mi?


Sanırım en son satırda fazladan bir çift kapatma ayracı ve paranteziniz var. Lütfen bunları silin ve tekrar deneyin.
Redu

4
Bu çok havalı, yeni işlenmemiş ret uyarısı gerçek hayatta hataları bulur ve insanlara zaman kazandırır. Burada çok fazla galibiyet. Bu uyarı olmasaydı, testleriniz herhangi bir açıklama yapılmadan zaman aşımına uğrayacaktı.
Benjamin Gruenbaum

Yanıtlar:


161

Sorun şundan kaynaklanıyor:

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

İddia başarısız olursa, bir hata atar. Bu hata done()asla aranmamasına neden olur çünkü kod ondan önce hata verir. Zaman aşımına neden olan budur.

"İşlenmeyen söz ret" da bir hata atılır çünkü eğer, öğe iddianın neden olur catch()işleyicisi, ve bir sonraki yoktur catch()işleyici (açıklandığı gibi, hata yutulması alacak bu makalede ). UnhandledPromiseRejectionWarningUyarı Bu gerçeği hakkında sizi uyaran bir.

Genel olarak, Mocha'da vaat temelli kodu test etmek istiyorsanız, Mocha'nın zaten vaatleri yerine getirebileceği gerçeğine güvenmelisiniz. Kullanmamalısın done(), bunun yerine testinden bir söz ver. Mocha daha sonra hataları kendisi yakalayacaktır.

Bunun gibi:

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
Merak edenler için bu aynı zamanda yasemin için de geçerlidir.
Nick Radford

@robertklep Yakalamak beklediğiniz hata değil, herhangi bir hata için çağrılmayacak mı? Başarısızlıkları öne sürmeye çalışıyorsanız bu tarzın işe yaramayacağını düşünüyorum.
TheCrazyProgrammer

1
@TheCrazyProgrammer catchişleyiciye muhtemelen ikinci argüman olarak aktarılmalıdır then. Ancak, OP'nin niyetinin ne olduğundan tam olarak emin değilim , bu yüzden olduğu gibi bıraktım.
robertklep

1
Ayrıca yasemini merak eden herkes için done.fail('msg')bu durumda kullanın .
Paweł

Ana kod ile iddiaların nereye gitmesi gerektiğine dair tam bir örnek verebilir misiniz, burada çok net değil ..... özellikle kodumuzdaki orijinal hizmet / vaat çağrısından bir değer ileri sürüyorsanız. Kodumuzun asıl sözü bu diğer "mocha sözü" kapsamında mı?
bjm88

10

Sinon ile saplarken bu hatayı aldım.

Düzeltme, vaat edildiği gibi npm sinon paketini kullanmaktır vaatleri taslaklarla veya reddederken vaat edildiği .

Onun yerine ...

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

Kullanım ...

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

Ayrıca bir çözümleme yöntemi de vardır (sondaki s'ye dikkat edin).

Bkz. Http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejections


1
Sinon artık sürüm 2'den itibaren stub'lar için "çözer" ve "reddeder" yöntemlerini içerir. Bkz. Npmjs.com/package/sinon-as-promised . Yine de yanıtı + 1'ledim - bunu bilmiyordum.
Andrew

9

Mocha'daki iddia kitaplıkları, iddia doğru değilse bir hata atarak çalışır. Bir hata atmak, catchyönteme sağlanan yürütücü işlevde atıldığında bile reddedilmiş bir sözle sonuçlanır .

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

Yukarıdaki kodda, erroritiraz edilen kişi değerlendirir, trueböylece iddia kütüphanesi hiçbir zaman yakalanmayan bir hata atar. Hatanın bir sonucu olarak, doneyöntem asla çağrılmaz. Mocha'nın donegeri araması bu hataları kabul eder, böylece Mocha'daki tüm söz zincirlerini basitçe sonlandırabilirsiniz .then(done,done). Bu, done yöntemin her zaman çağrılmasını ve hatanın, Mocha'nın senkron kodda iddianın hatasını yakalamasıyla aynı şekilde raporlanmasını sağlar.

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);
});

Mocha'da vaatleri test ederken .then (bitti, bitti) kullanma fikri için bu makaleye kredi veriyorum .


6

Hatayı / uyarıyı UnhandledPromiseRejectionWarningbir test ortamı dışında arayanlar için, bunun nedeni muhtemelen koddaki hiç kimsenin bir vaatteki nihai hatayla ilgilenmemesi olabilir:

Örneğin, bu kod, bu soruda bildirilen uyarıyı gösterecektir:

new Promise((resolve, reject) => {
  return reject('Error reason!');
});

(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!

ve .catch()hatanın eklenmesi veya işlenmesi, uyarıyı / hatayı çözmelidir

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });

Veya thenişlevdeki ikinci parametreyi kullanarak

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });

1
Tabii ki, ancak gerçek hayatta genellikle sadece kullanmadığımızı, new Promise((resolve, reject) => { return reject('Error reason!'); })ancak işlevde kullanmadığımızı düşünüyorum, function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}bu yüzden işlev içinde kullanmamız gerekmiyor, .catch()ancak hataları başarılı bir şekilde ele almak için bu işlevi test().catch(e => console.log(e))veya zaman uyumsuz / try { await test() } catch (e) { console.log(e) }
bekleme

1

Bu sorunla karşılaştım:

(düğüm: 1131004) UnhandledPromiseRejectionWarning: İşlenmemiş söz reddi (re jection id: 1): TypeError: res.json bir işlev değil (düğüm: 1131004) DeprecationWarning: İşlenmemiş taahhüt redleri kullanımdan kaldırıldı. Gelecekte, işlenmeyen vaat retleri, Node.j s işlemini sıfır olmayan bir çıkış koduyla sonlandıracaktır.

Benim hatamdı, resiçindeki nesneyi değiştiriyordum then(function(res), çok değiştimres sonuca ve şimdi çalışıyor.

Yanlış

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");
              });

Düzeltme

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");
              });

Servis kodu:

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

İşte benim E7 async / await ile deneyimim :

async helperFunction()Testinizden bir çağrı almanız durumunda ... (ES7 asyncanahtar kelimesi ile bir açıklama, yani)

→ emin olun, bunu olarak adlandırın await helperFunction(whateverParams) (evet, doğal olarak, öğrendikten sonra ...)

Ve bunun işe yaraması için ('bekleme, ayrılmış bir kelimedir' den kaçınmak için), test işlevinizin bir dış zaman uyumsuz işaretçisi olmalıdır:

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

4
Sen yok olması olarak adlandırmak await helperFunction(...). Bir asyncişlev bir söz verir. asyncBir sözün geri dönmesi için işaretlenmemiş bir işlevde yaptığınız gibi iade edilen sözü yerine getirebilirsiniz . Önemli olan sözü yerine getirmektir, nokta. Fonksiyonun olup asyncolmadığı önemli değil. awaitbu , sözü yerine getirmenin birçok yolundan yalnızca biridir .
Louis

1
Doğru. Ama sonra yakalamaya yatırım yapmalıyım ... yoksa testlerim yanlış pozitifler olarak geçer ve başarısız sözler (testrunner sonuçları açısından) ertelenmez. Bekle bana daha az çizgi ve çaba gibi görünüyor. - Her neyse, beklemeyi unutmak UnhandledPromiseRejectionWarningbenim için buna neden oldu ... bu yüzden bu cevap.
Frank Nocke

0

Selenium için Chai-Webdriver ile benzer bir deneyimim oldu. awaitİddiaya ekledim ve sorunu çözdü:

Cucumberjs kullanma örneği:

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

-7

Webpack'i kaldırdıktan sonra bu sorunu çözdüm (js sorununa tepki ver).

sudo uninstall webpack
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.