NodeJS UnhandledPromiseRejectionWarning


134

그래서 이벤트 이미 터를 사용하는 구성 요소를 테스트하고 있습니다. 그렇게하기 위해 Mocha + Chai와 함께 Promises를 사용하는 솔루션을 생각해 냈습니다.

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

콘솔에 'AssertionError : Promise error'메시지가 즉시 표시되므로 거부 함수가 호출되었지만 'UnhandledPromiseRejectionWarning'이 나타납니다.

(node ​​: 25754) UnhandledPromiseRejectionWarning : 처리되지 않은 약속 거부 (거부 ID : 2) : AssertionError : 약속 오류 : 예상 {개체 (메시지, showDiff, ...)}가 거짓 1 임) 올바른 이벤트로 전환해야 함

그리고 2 초 후

오류 : 시간 초과 2000ms를 초과했습니다. 이 테스트에서 done () 콜백이 호출되고 있는지 확인하십시오.

catch 콜백이 실행 된 이후로 더 이상합니다 (어떤 이유로 든 어설 션 실패로 인해 나머지 실행이 차단되었다고 생각합니다)

이제 재미있는 것은 assert.isNotOk(error...)콘솔에서 경고없이 테스트가 정상적으로 실행 된다는 것 입니다. 캐치를 실행한다는 의미에서 여전히 '실패'합니다.
그러나 여전히, 나는 이러한 오류를 약속으로 이해할 수 없습니다. 누군가 나를 밝게 할 수 있습니까?


마지막 줄에 닫는 중괄호와 파렌이 하나 더 있다고 생각합니다. 삭제 한 후 다시 시도하십시오.
Redu

4
처리되지 않은 새로운 거부 경고는 실제로 버그를 발견하고 사람들의 시간을 절약 해줍니다. 여기에서 너무 많은 승리. 이 경고가 없으면 테스트가 설명없이 시간 초과 된 것입니다.
Benjamin Gruenbaum

답변:


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
궁금한 사람에게는 자스민에게도 해당됩니다.
Nick Radford

@robertklep 당신이 기대하는 오류뿐만 아니라 어떤 오류가 발생하지 않습니까? 실패를 주장하려는 경우이 스타일이 작동하지 않는다고 생각합니다.
TheCrazyProgrammer 2018 년

1
@TheCrazyProgrammer catch핸들러는 아마도 두 번째 인자로 전달되어야합니다 then. 그러나 OP의 의도가 무엇인지 완전히 확신 하지 못 하므로 그대로 두었습니다.
robertklep

1
또한 자스민 done.fail('msg')에 관심이 있는 사람 은이 경우에 사용하십시오.
Paweł

메인 코드 대 어설 션이 어디로 가야하는지에 대한 전체 예를 제공 할 수 있습니다. 특히 명확하지는 않습니다. 특히 코드에서 원래 서비스 / 약속 호출에서 값을 주장하는 경우. 코드에 대한 실제 약속이 다른 "모카 약속"내에 있습니까?
bjm88

10

sinon으로 스터 빙 할 때이 오류가 발생했습니다.

해결책은 스텁으로 약속을 해결하거나 거부 할 때 약속대로 npm 패키지를 사용 하는 것입니다.

대신에 ...

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

사용하다 ...

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

resolves 메소드도 있습니다 (끝에 s를보십시오).

http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-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메소드가 호출되지 않습니다. 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);
});

Mocha에서 약속을 테스트 할 때 .then (done, done)을 사용한다는 아이디어에 대해이 기사 를 신뢰합니다 .


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 / await 버전을 호출 할 때 사용하기에 충분합니다.try { await test() } catch (e) { console.log(e) }
mikep

1

나는이 문제에 직면했다 :

(node ​​: 1131004) UnhandledPromiseRejectionWarning : 처리되지 않은 약속 거부 (판정 ID : 1) : TypeError : res.json은 함수가 아닙니다 (노드 : 1131004) DeprecationWarning : 처리되지 않은 약속 거부는 더 이상 사용되지 않습니다. 앞으로 처리되지 않은 약속 거부는 0이 아닌 종료 코드로 Node.j 프로세스를 종료합니다.

실수였습니다. 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 / await에 대한 나의 경험은 다음과 같습니다 .

async helperFunction()테스트에서 전화를 한 경우 ... (ES7 async키워드에 대한 설명이 하나 있습니다.)

→ 확인하십시오. await helperFunction(whateverParams) (알다시피 자연스럽게, 일단 알면 ...)

그리고 그것이 작동하려면 ( '기다리는 예약어'를 피하기 위해) 테스트 함수에 외부 비동기 마커가 있어야합니다.

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

4
당신은하지 않습니다 로 전화를 await helperFunction(...). async함수는 약속을 반환합니다. 약속 async을 반환하는 것으로 표시되지 않은 함수에서와 같이 반환 된 약속을 처리 할 수 ​​있습니다 . 요점은 약속, 기간을 처리하는 것입니다. 기능이 async중요하지 않은지 여부 약속을 처리하는 여러 가지 방법 중 하나await 일뿐 입니다.
Louis

1
진실. 그러나 캐치 라인에 투자해야합니다 ... 또는 내 테스트가 오탐으로 통과하고 실패한 약속은 모두 테스트되지 않은 결과로 나타납니다 (testrunner 결과 측면에서). 그래서 기다림은 줄어든다. – 어쨌든, 기다림을 잊어 버린 것은 UnhandledPromiseRejectionWarning저 를 위한 원인 이었습니다 ... 따라서이 답변입니다.
Frank Nocke

0

Chai-Webdriver for Selenium과 비슷한 경험을했습니다. await어설 션에 추가 하고 문제를 해결했습니다.

Cucumberjs를 사용한 예 :

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

-7

webpack을 제거한 후이 문제를 해결했습니다 (js 문제 해결).

sudo uninstall webpack
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.