NodeJS UnhandledPromiseRejectionWarning

134

Jadi, saya menguji komponen yang bergantung pada emitor-acara. Untuk melakukannya saya datang dengan solusi menggunakan Janji dengan 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();
  });
});

Pada konsol saya mendapatkan 'UnhandledPromiseRejectionWarning' meskipun fungsi tolak dipanggil karena langsung menampilkan pesan 'AssertionError: Promise error'

(simpul: 25754) UnhandledPromiseRejectionWarning: Penolakan janji yang tidak ditangani (id penolakan: 2): AssertionError: Galat janji: diharapkan {Obyek (pesan, showDiff, ...)} menjadi falsy 1) harus beralih dengan kejadian yang benar

Dan kemudian, setelah 2 detik saya dapatkan

Kesalahan: batas waktu 2000 ms terlampaui. Pastikan panggilan balik yang dilakukan () dipanggil dalam tes ini.

Yang bahkan lebih aneh sejak catchbackback dieksekusi. (Saya pikir karena alasan tertentu kegagalan yang gagal mencegah sisa eksekusi)

Nah, yang lucu, jika saya berkomentar assert.isNotOk(error...)tes ini berjalan dengan baik tanpa ada peringatan di konsol. Masih 'gagal' dalam arti bahwa itu mengeksekusi tangkapan.
Tapi tetap saja, saya tidak bisa memahami kesalahan ini dengan janji. Bisakah seseorang mencerahkan saya?

Jzop
sumber
Saya pikir Anda memiliki satu set tambahan kurung kurawal dan paren di baris terakhir. Silakan hapus dan coba lagi.
Redu
4
Ini sangat keren, peringatan penolakan yang tidak tertangani baru menemukan bug dalam kehidupan nyata dan menghemat waktu orang. Banyak yang menang di sini. Tanpa peringatan ini tes Anda akan habis tanpa penjelasan.
Benjamin Gruenbaum

Jawaban:

161

Masalahnya disebabkan oleh ini:

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

Jika pernyataan gagal, itu akan menimbulkan kesalahan. Kesalahan ini akan menyebabkan done()tidak pernah dipanggil, karena kode salah sebelum itu. Itulah yang menyebabkan batas waktu.

The "tertangani janji penolakan" juga disebabkan oleh pernyataan gagal, karena jika kesalahan dilemparkan dalam catch()handler, dan tidak ada berikutnya catch()handler , kesalahan akan mendapatkan ditelan (seperti yang dijelaskan dalam artikel ini ). The UnhandledPromiseRejectionWarningperingatan memperingatkan Anda untuk fakta ini.

Secara umum, jika Anda ingin menguji kode berbasis janji dalam Mocha, Anda harus mengandalkan fakta bahwa Mocha sendiri sudah dapat menangani janji. Anda seharusnya tidak menggunakan done(), tetapi sebaliknya, mengembalikan janji dari ujian Anda. Mocha kemudian akan menangkap kesalahan itu sendiri.

Seperti ini:

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');
  });
});
robertklep
sumber
7
Bagi siapa pun yang penasaran, ini juga berlaku untuk melati.
Nick Radford
@robertklep Tidak akan menangkap akan dipanggil untuk kesalahan dan bukan hanya yang Anda harapkan? Saya pikir gaya ini tidak akan berfungsi jika Anda mencoba untuk menyatakan kegagalan.
TheCrazyProgrammer
1
@TheCrazyProgrammer, catchpawang mungkin harus diteruskan sebagai argumen kedua then. Namun, saya tidak sepenuhnya yakin apa maksud OP, jadi saya membiarkannya apa adanya.
robertklep
1
Dan juga bagi siapa pun yang ingin tahu tentang melati, gunakan done.fail('msg')dalam kasus ini.
Paweł
dapatkah Anda memberikan contoh lengkap di mana kode utama vs pernyataan harus dituju, tidak begitu jelas di sini ..... terutama jika Anda menegaskan nilai dari layanan asli / panggilan janji dalam kode kami. Apakah janji sebenarnya untuk kode kita masuk dalam "janji moka" lainnya ini ??
bjm88
10

Saya mendapat kesalahan ini ketika membandel dengan sinon.

Cara mengatasinya adalah dengan menggunakan paket npm sinon seperti yang dijanjikan saat menyelesaikan atau menolak janji dengan bertopik.

Dari pada ...

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

Gunakan ...

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

Ada juga metode resolves (perhatikan huruf s pada bagian akhir).

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

danday74
sumber
1
Sinon sekarang menyertakan metode "resolves" dan "reject" untuk bertopik pada versi 2. Lihat npmjs.com/package/sinon-as-promised . Namun, saya masih memberi +1 jawabannya - saya tidak tahu tentang ini.
Andrew
9

Pustaka pernyataan di Mocha bekerja dengan melemparkan kesalahan jika pernyataan itu tidak benar. Melempar kesalahan menghasilkan janji yang ditolak, bahkan ketika dilempar ke fungsi pelaksana yang disediakan untuk catchmetode ini.

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

Dalam kode di atas, errorkeberatan dievaluasi truesehingga perpustakaan pernyataan melempar kesalahan ... yang tidak pernah tertangkap. Sebagai akibat dari kesalahan donemetode tidak pernah dipanggil. doneCallback Mocha menerima kesalahan ini, jadi Anda bisa dengan mudah mengakhiri semua rantai janji di Mocha .then(done,done). Ini memastikan bahwa metode yang dilakukan selalu dipanggil dan kesalahan akan dilaporkan dengan cara yang sama seperti ketika Mocha menangkap kesalahan pernyataan dalam kode sinkron.

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

Saya memberikan kredit untuk artikel ini untuk ide menggunakan. Maka (selesai, dilakukan) ketika menguji janji dalam Mocha.

Matthew Orlando
sumber
6

Bagi mereka yang mencari kesalahan / peringatan di UnhandledPromiseRejectionWarningluar lingkungan pengujian, bisa jadi mungkin karena tidak ada dalam kode yang menangani kesalahan akhirnya dalam sebuah janji:

Misalnya, kode ini akan menampilkan peringatan yang dilaporkan dalam pertanyaan ini:

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

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

dan menambahkan .catch()atau menangani kesalahan harus menyelesaikan peringatan / kesalahan

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

Atau menggunakan parameter kedua dalam thenfungsi

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
gsalgadotoledo
sumber
1
Tentu saja tetapi saya pikir dalam kehidupan nyata kita biasanya tidak menggunakan hanya new Promise((resolve, reject) => { return reject('Error reason!'); })tetapi dalam fungsi function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}jadi di dalam fungsi kita tidak perlu menggunakan .catch()tetapi untuk berhasil menangani kesalahan itu cukup untuk digunakan saat memanggil fungsi itu test().catch(e => console.log(e))atau versi async / menunggutry { await test() } catch (e) { console.log(e) }
mikep
1

Saya menghadapi masalah ini:

(simpul: 1131004) UnhandledPromiseRejectionWarning: Penolakan janji yang tidak tertangani (ulang id id: 1): TypeError: res.json bukan fungsi (node: 1131004) DeprecationWarning: penolakan janji tidak tertangani sudah ditinggalkan. Di masa depan, penolakan janji yang tidak ditangani akan menghentikan proses Node.j dengan kode keluar yang tidak nol.

Itu kesalahan saya, saya mengganti resobjek then(function(res), jadi berubah reshasilnya dan sekarang berfungsi.

Salah

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

Koreksi

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

Kode layanan:

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
}
Muhammad Shahzad
sumber
1

Inilah pengalaman saya mengambil dengan E7 async / menunggu :

Jika Anda memiliki panggilan async helperFunction()dari pengujian Anda ... (satu alasan dengan ES7 asynckata kunci, maksud saya)

→ pastikan, Anda menyebutnya demikian await helperFunction(whateverParams) (ya, tentu saja, begitu Anda tahu ...)

Dan agar itu berfungsi (untuk menghindari 'menunggu adalah kata yang dilindungi undang-undang'), fungsi uji Anda harus memiliki penanda async luar:

it('my test', async () => { ...
Frank Nocke
sumber
4
Anda tidak harus menyebutnya sebagai await helperFunction(...). Suatu asyncfungsi mengembalikan janji. Anda bisa menangani janji yang dikembalikan seperti yang Anda lakukan pada fungsi yang tidak ditandai asyncyang terjadi untuk mengembalikan janji. Intinya adalah untuk menangani janji, titik. Apakah fungsinya asyncatau tidak tidak masalah. awaithanyalah satu di antara banyak cara untuk menangani janji.
Louis
1
Benar. Tapi kemudian saya harus menginvestasikan garis pada penangkapan ... atau tes saya lulus sebagai positif palsu, dan setiap janji yang gagal akan pergi tanpa bantuan (dalam hal hasil testrunner). Jadi tunggu sepertinya kurang garis & usaha bagiku. - Ngomong-ngomong, lupa menunggu adalah, apa yang menyebabkan itu UnhandledPromiseRejectionWarninguntukku ... jadi jawaban ini.
Frank Nocke
0

Saya memiliki pengalaman serupa dengan Chai-Webdriver for Selenium. Saya menambahkan awaitpernyataan dan memperbaiki masalah:

Contoh menggunakan Cucumberjs:

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

Saya memecahkan masalah ini setelah menghapus instalan webpack (bereaksi masalah js).

sudo uninstall webpack
Tuan Menyenangkan
sumber