NodeJS UnhandledPromiseRejectionWarning


134

Vì vậy, tôi đang thử nghiệm một thành phần dựa trên trình phát sự kiện. Để làm như vậy, tôi đã đưa ra một giải pháp bằng cách sử dụng Promising với 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();
  });
});

Trên bảng điều khiển, tôi nhận được một 'UnhandledPromiseRejectionWarning' mặc dù chức năng từ chối đang được gọi vì nó ngay lập tức hiển thị thông báo 'AssertsError: Promise error'

(nút: 25754) UnhandledPromiseRejectionWarning: Từ chối lời hứa chưa được xử lý (id từ chối: 2): AssertsError: Lỗi hứa hẹn: dự kiến ​​{Object (tin nhắn, showDiff, ...)} sẽ chuyển đổi với sự kiện chính xác

Và sau 2 giây tôi nhận được

Lỗi: quá thời gian chờ 2000ms. Đảm bảo gọi lại thực hiện () đang được gọi trong thử nghiệm này.

Điều này thậm chí còn kỳ lạ hơn kể từ khi cuộc gọi lại bắt được thực thi. (Tôi nghĩ rằng vì một số lý do, sự thất bại khẳng định đã ngăn cản phần còn lại của việc thực hiện)

Bây giờ điều buồn cười, nếu tôi nhận xét assert.isNotOk(error...)bài kiểm tra chạy tốt với bất kỳ cảnh báo nào trong bảng điều khiển. Nó vẫn "thất bại" theo nghĩa là nó thực hiện việc bắt.
Nhưng tôi vẫn không thể hiểu những lỗi này bằng lời hứa. Ai đó có thể khai sáng cho tôi?


Tôi nghĩ rằng bạn có thêm một bộ nẹp đóng và parens ở dòng cuối cùng. Vui lòng xóa chúng và thử lại.
Redu

4
Điều này thật tuyệt, cảnh báo từ chối mới chưa được xử lý tìm thấy lỗi trong cuộc sống thực và tiết kiệm thời gian của mọi người. Vì vậy, nhiều chiến thắng ở đây. Nếu không có cảnh báo này, các bài kiểm tra của bạn sẽ hết thời gian mà không có lời giải thích nào.
Benjamin Gruenbaum

Câu trả lời:


161

Vấn đề được gây ra bởi điều này:

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

Nếu xác nhận thất bại, nó sẽ đưa ra một lỗi. Lỗi này sẽ khiến done()không bao giờ được gọi, vì mã bị lỗi trước nó. Đó là những gì gây ra thời gian chờ.

Các "từ chối lời hứa Unhandled" cũng là do sự khẳng định thất bại, bởi vì nếu một lỗi được ném trong một catch()handler, và không có một kế tiếp catch()xử lý , lỗi sẽ bị nuốt (như đã giải thích trong bài viết này ). Các UnhandledPromiseRejectionWarningcảnh báo được cảnh báo cho bạn về việc này.

Nói chung, nếu bạn muốn kiểm tra mã dựa trên lời hứa trong Mocha, bạn nên dựa vào thực tế là chính Mocha có thể xử lý các lời hứa. Bạn không nên sử dụng done(), nhưng thay vào đó, hãy trả lại lời hứa từ bài kiểm tra của bạn. Mocha sau đó sẽ tự bắt bất kỳ lỗi nào.

Như thế này:

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
Đối với bất kỳ ai tò mò, điều này cũng đúng với hoa nhài.
Nick Radford

@robertklep Không bắt được sẽ được gọi cho bất kỳ lỗi nào và không chỉ là lỗi bạn mong đợi? Tôi nghĩ phong cách này sẽ không hiệu quả nếu bạn đang cố khẳng định thất bại.
TheCrazyProgrammer

1
@TheCrazyProgrammer catchtrình xử lý có thể được chuyển qua làm đối số thứ hai then. Tuy nhiên, tôi không hoàn toàn chắc chắn ý định của OP là gì, vì vậy tôi đã để nguyên như vậy.
robertklep

1
Và cũng cho bất cứ ai tò mò về hoa nhài, sử dụng done.fail('msg')trong trường hợp này.
Paweł

bạn có thể đưa ra ví dụ đầy đủ về nơi mã chính so với các xác nhận sẽ đi, không rõ ràng ở đây ..... đặc biệt nếu bạn xác nhận một giá trị từ dịch vụ ban đầu / cuộc gọi lời hứa trong mã của chúng tôi. Liệu lời hứa thực sự cho mã của chúng tôi có nằm trong "lời hứa mocha" khác này không ??
bjm88

10

Tôi đã nhận được lỗi này khi khai thác với sinon.

Cách khắc phục là sử dụng gói npm sinon như đã hứa khi giải quyết hoặc từ chối lời hứa với cuống.

Thay vì ...

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

Sử dụng ...

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

Ngoài ra còn có một phương thức giải quyết (lưu ý s ở cuối).

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


1
Sinon hiện bao gồm các phương thức "giải quyết" và "từ chối" cho các sơ khai kể từ phiên bản 2. Xem npmjs.com/package/sinon-as-promised . Mặc dù vậy, tôi vẫn trả lời + 1 câu trả lời - Tôi không biết về điều này.
Andrew

9

Các thư viện xác nhận trong Mocha hoạt động bằng cách đưa ra lỗi nếu xác nhận không đúng. Ném một lỗi dẫn đến một lời hứa bị từ chối, ngay cả khi ném vào hàm thực thi được cung cấp cho catchphương thức.

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

Trong đoạn mã trên, errorđối tượng ước tính trueđể thư viện xác nhận đưa ra một lỗi ... không bao giờ bị bắt. Do lỗi, donephương thức không bao giờ được gọi. Cuộc donegọi lại của Mocha chấp nhận các lỗi này, vì vậy bạn chỉ cần kết thúc tất cả các chuỗi lời hứa trong Mocha bằng .then(done,done). Điều này đảm bảo rằng phương thức thực hiện luôn được gọi và lỗi sẽ được báo cáo giống như khi Mocha bắt lỗi xác nhận trong mã đồng bộ.

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

Tôi cung cấp tín dụng cho bài viết này cho ý tưởng sử dụng .then (đã hoàn thành, đã hoàn thành) khi thử nghiệm các lời hứa trong Mocha.


6

Đối với những người đang tìm kiếm lỗi / cảnh báo UnhandledPromiseRejectionWarningbên ngoài môi trường thử nghiệm, có thể là do không ai trong mã đang quan tâm đến lỗi cuối cùng trong một lời hứa:

Chẳng hạn, mã này sẽ hiển thị cảnh báo được báo cáo trong câu hỏi này:

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

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

và thêm .catch()hoặc xử lý lỗi sẽ giải quyết cảnh báo / lỗi

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

Hoặc sử dụng tham số thứ hai trong thenhàm

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

1
Tất nhiên nhưng tôi nghĩ trong cuộc sống thực, chúng ta thường không sử dụng new Promise((resolve, reject) => { return reject('Error reason!'); })mà chỉ sử dụng chức năng function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}nên bên trong chức năng chúng ta không cần sử dụng .catch()nhưng để xử lý thành công lỗi đó là đủ để sử dụng chức năng đó test().catch(e => console.log(e))hoặc phiên bản async / awaittry { await test() } catch (e) { console.log(e) }
mikep

1

Tôi đã đối mặt với vấn đề này:

(nút: 1131004) UnhandledPromiseRejectionWarning: Từ chối lời hứa chưa được xử lý (re jection id: 1): TypeError: res.json không phải là một chức năng (nút: 1131004) Không được chấp nhận Trong tương lai, các từ chối hứa hẹn không được xử lý sẽ chấm dứt quá trình Node.j với mã thoát không khác không.

Đó là sai lầm của tôi, tôi đã thay thế resđối tượng then(function(res), vì vậy đã thay đổi resthành kết quả và bây giờ nó đang hoạt động.

Sai lầm

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

Điều chỉnh

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

Mã dịch vụ:

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

Đây là kinh nghiệm của tôi với E7 async / await :

Trong trường hợp bạn có một cuộc async helperFunction()gọi từ bài kiểm tra của mình ... ( asyncý tôi là một từ khóa với ES7 )

→ đảm bảo, bạn gọi nó là await helperFunction(whateverParams) (tốt, vâng, một cách tự nhiên, một khi bạn biết ...)

Và để điều đó hoạt động (để tránh 'chờ đợi là một từ dành riêng'), chức năng kiểm tra của bạn phải có điểm đánh dấu không đồng bộ bên ngoài:

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

4
Bạn không phải gọi nó là await helperFunction(...). Một asynchàm trả về một lời hứa. Bạn chỉ có thể xử lý lời hứa được trả lại giống như bạn thực hiện trên một chức năng không được đánh dấu asyncxảy ra để trả lại lời hứa. Vấn đề là xử lý lời hứa, thời gian. Cho dù chức năng là asynchay không không quan trọng. awaitchỉ là một trong nhiều cách để xử lý lời hứa.
Louis

1
Thật. Nhưng sau đó tôi phải đầu tư các dòng để bắt ... hoặc các bài kiểm tra của tôi vượt qua là dương tính giả, và bất kỳ lời hứa thất bại nào cũng sẽ không được chứng minh (về kết quả của người thử nghiệm). Vì vậy, chờ đợi có vẻ như ít dòng và nỗ lực với tôi. - Dù sao, quên chờ đợi là, điều gì gây ra điều đó UnhandledPromiseRejectionWarningcho tôi ... vì vậy câu trả lời này.
Frank Nocke

0

Tôi đã có một trải nghiệm tương tự với Chai-Webdo cho Selenium. Tôi đã thêm vào awaitxác nhận và nó đã khắc phục vấn đề:

Ví dụ sử dụng Cucumberjs:

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

-7

Tôi đã giải quyết vấn đề này sau khi gỡ cài đặt webpack (phản ứng vấn đề js).

sudo uninstall webpack
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.