简体   繁体   English

如何用回调包装Promise?

[英]How to wrap a promise with a callback?

In the company I work in, everything is currently done with callbacks. 在我工作的公司中,目前所有工作都使用回调。 We are starting to write small components with promises that the big code depends of. 我们开始编写小型组件,并承诺大型代码所依赖。 We started having trouble with them. 我们开始遇到麻烦。

function getSomething() {
  return Promise.resolve('hello')
}

function test(cb) {
  getSomething()
  .then(string => {
    a.s
    cb(null, string)
  }, error => cb(error))
}

test((error, result) => {
  console.log(error)
  console.log(result)
  a.s
})

This is a simple example of the problem. 这是问题的简单示例。 In this code since a does not exist it would throw a warning UnhandledPromiseRejectionWarning and kill the process. 在此代码中,由于a不存在,它将引发警告UnhandledPromiseRejectionWarning并终止该进程。 console logs are never reached. 控制台日志永远不会到达。

The logic behind was that if an error ever happens it would trigger the catch callback. 背后的逻辑是,如果发生错误,将触发catch回调。

function test(cb) {
  getSomething()
  .then(string => {
    // a.s
    cb(null, string)
  }, error => cb(error))
  .catch(error => cb(error))
}

I was advised to use the explicit catch at the end of the promise chain. 建议我在promise链的末尾使用显式捕获。 The problem is if in the callback an error is thrown, the callback would trigger twice. 问题是,如果在回调中引发错误,则回调将触发两次。

Thanks for any help. 谢谢你的帮助。

There is a subtle difference between 两者之间有细微的差别

.then(onFulfilled, onRejected)

and

.then(onFullfilled)
.catch(onRejected)

The first one wouldn't be able to catch the errors thrown within the onFullFilled callback while the second one would. 第一个无法捕获onFullFilled回调中引发的错误,而第二个则可以。 So may be you should not use the onRejected callback at the then stage and just chain a .catch() like you did in your second snippet. 因此,也许您不应该在onRejected使用onRejected回调,而应该像在第二个片段中那样链接.catch()

You could separate the code in the then part so that errors before calling cb are treated differently than the ones that occur within cb . 你可以在代码中分离出来then让调用CB前误差比CB内发生的那些区别对待的一部分。 Instead of calling cb there, you would return the string value so that it can be used in a chained then method. 您可以返回字符串值,而不是在此处调用cb ,以便可以在链接的then方法中使用它。 And there you can use the switch between success and failure. 在这里,您可以在成功与失败之间进行切换。 That way cb will only be called there. 这样, cb将仅在此处被调用。

Now if by calling cb an error occurs, you could catch that with a final .catch , but then you would not call cb anymore, but maybe just output something, or cascade the error, or do whatever you want to do in that case. 现在,如果通过调用cb发生错误,则可以使用最终的.catch捕获该错误,但是您将不再调用cb ,而只是输出某些内容,或者级联错误,或者在这种情况下可以做任何想做的事情。

Here is a demo with three use cases: 这是一个包含三个用例的演示:

  1. No error 没错
  2. An error in the (first) then then (第一个)出现错误
  3. An error in cb cb中的错误

 function getSomething() { return Promise.resolve('hello'); } function test(testNo, cb) { getSomething() .then(string => { if (testNo == 2) as; return string; // don't call cb yet }) .then(cb.bind(null, null), cb) // now call it -- mutually exclusive with error case .catch(error => { // catch any errors not yet captured -- ie in cb console.log('test ' + testNo + ': error occurred in success callback'); }); } for (let testNo = 1; testNo <= 3; testNo++) { test(testNo, (error, result) => { if (error) console.log('callback for test ' + testNo + ' error:', error.message); else console.log('callback for test ' + testNo + ' success:', result); if (testNo == 3) as; }); } 

What happens in your code snippet is, that you return a rejected promise when an error occurs within your then() . 您的代码段中发生的是,当then()发生错误时,您将返回被拒绝的承诺。 And as you don't have any handler after that, this rejected promise is not handled and (fortunately) pops up. 而且由于此后您没有任何处理程序,因此不会处理这个被拒绝的诺言,并且(幸运地)会弹出。

You mainly have two possibilities to fix this: You could either add another catch() after your then() method like this 您主要有两种方法可以解决此问题:您可以在then()方法之后添加另一个catch()这样

function test(cb) {
  getSomething()
  .then(string => {
    a.s
    cb(null, string)
  })
  .catch(e => cb)   //this catch() handles whatever rejected promise
                    //comes out of then() or from the earlier promise
}

In this solution it may happen, that you get your callback called twice in case an error is thrown while executing cb(null, string). 在这种解决方案中,您可能会执行两次回调,以防执行cb(null,string)时抛出错误。 In that case you need a guard in your callback to distinguish different error codes to find out whether it comes from getSomething() or from cb(null, string). 在这种情况下,您需要在回调函数中使用防护措施来区分不同的错误代码,以查明它是来自getSomething()还是来自cb(null,string)。

Or you could add a traditional try/catch-block within your then() handler like this 或者,您可以像这样在then()处理函数中添加传统的try / catch-block

function test(cb) {
  getSomething()
  .then(string => {
    try{
       a.s
       cb(null, string)
    } catch(e) {
       if(e.message == "callback error" {
          //do what you want to do to with a callback error
       } else {
          //do whatever you want to do with other errors from your catch block
       }
    }
  }, error => cb(error))
}

In this solution you can put the guard to distinguish different error reasons inside the catch block and have the callback called only once. 在此解决方案中,您可以使用防护措施来区分catch块内的各种错误原因,并使回调仅被调用一次。 However you need another handling mechanism to handle your errors. 但是,您需要另一种处理机制来处理错误。 One easy possibility would be to silently swallow the new errors, however this might be a bad solution. 一种简单的可能性是静默吞下新错误,但这可能是一个不好的解决方案。

That's the problem with asynchronous programming, that there is no possibility for errors to bubble up to a central handler as the stack context is lost till the callback is finally executed. 这就是异步编程的问题,因为栈上下文会丢失,直到最终执行回调为止,错误才可能冒泡到中央处理程序。

Cheers, Felix 干杯,菲利克斯

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM