How do I know which handlers throw error in promise?

无人久伴 提交于 2020-12-11 05:02:53

问题


Suppose I have a promise as following:

p.then(Task1)
 .then(Task2)
 .then(Task3)
 .catch(errorHandler);

When Task2 encounters error, how do I know the error is from Task2 in catch?


回答1:


everyone! I had researched demonstrated code by myself.

I hoped everyone can review my answer, it's good or not.


Introduction:

It shows how to trace promise in each handler, used customized error handler to catch error. To understand the workflow of promise.

You can copy the following demonstrated code and paste in your node.js. According to the example and log message, it's good for the developers to learn promise.


The used promise module is as following:

  • bluebird

The demonstrated code is as following:

var Promise = require('bluebird');

// You can input any argument in this function to test workflow of promise 
function testPromise(input) {
    let promise = Promise.resolve(input);
    promise
      .then(makeTask('Task1'))
      .then(makeTask('Task2'))
      .then(makeTask('Task3'))
      .catch(makeErrorPredicate('Task1'), taskLog('Task1'))
      .catch(makeErrorPredicate('Task2'), taskLog('Task2'))
      .catch(makeErrorPredicate('Task3'), taskLog('Task3'))
}

// define task handler functions
function makeTask(task) {
    return function task1Handler(input) {
        if (input === task) {
            throw new Error(task)
        }            
        return input
    }
}

// custom error that it checks error message 
function makeErrorPredicate(msg) {
    return function taskError(err) {
        var result = err.message === msg;
        console.log(msg + ': ' + result)
        return result;
    }
}

// hint the error when the error has matched
function taskLog(msg) {
    return function thelog(err) {
        console.log('It\'s ' + msg)
    }  
}

The example:

>testPromise('Task1')
Task1: true
It's Task1

>testPromise('Task2')
Task1: false
Task2: true
It's Task2

>testPromise('Task3')
Task1: false
Task2: false
Task3: true
It's Task3

From the example above we can know:

When input is 'Task1', the route is:

firstHandler -> firstCatcher

When input is 'Task2', the route is:

firstHandler -> secondHandler -> firstCatcher -> secondCather

When input is 'Task3', the route is:

firstHandler -> secondHandler -> thirdHandler -> firstCatcher -> secondCatcher -> thirdCatcher

So, from the result above we know, we can understand the promise how to work.


If everyone is happy with this answer or not, please let me know, thanks.




回答2:


Since a promise chain cannot hold this information, you need to store it somewhere. Two solutions:

Decorating the error:

p.then(Task1)
 .then(function() {
    try {
      Task2();
    } catch (e) {
      e.isFromTask2 = true;
      throw e;
    } 
  })
 .then(Task3)
 .catch(errorHandler); // now you can check err.isFromTask2

Using a scope for the promise chain:

{
  let isFromTask2 = false;
  p.then(Task1)
   .then(function() {
      try {
        Task2();
      } catch (e) {
        isFromTask2 = true;
        throw e;
      } 
    })
   .then(Task3)
   .catch(errorHandler); // now you can check isFromTask2
}



回答3:


Most browsers support the error.stack property, and some modern browsers even support asynchronous callstacks:

let a = () => Promise.resolve();
let b = () => Promise.reject(new Error("Fail"));
let c = () => Promise.resolve();

Promise.resolve().then(a).then(b).then(c).catch(e => console.log(e.stack));

In Firefox 48 it outputs: b@http://stacksnippets.net/js:14:30.

In Chrome 52 it outputs: Error: Fail at b (http://stacksnippets.net/js:14:30).

This is the most idiomatic solution, as it doesn't interfere with how code is written. Unfortunately, not all browsers support asynchronous callstacks yet, and the output varies a bit from browser to browser, as it's meant for debugging.




回答4:


In order to know an (asynchronous) error in the final catch came from Task2 in all browsers, you can catch it, tag it, and rethrow it:

var tag = (e, name) => (e.isFrom = name, Promise.reject(e));

p.then(Task1)
 .then(Task2)
 .catch(e => tag(e, "Task2"))
 .then(Task3)
 .catch(errorHandler);

Unfortunately, this will catch errors from Task1 and p as well, so you also need a "catch-bypass":

p.then(Task1)
 .then(result => Task2(result).catch(e => tag(e, "Task2")))
 .then(Task3)
 .catch(errorHandler);

This makes any error from Task1 or earlier "go around" (i.e. bypass) our catch of Task2.

This works because we put the catch inside the success callback of .then. Every .then has an implicit second argument which is an error handler, much like with .catch, except it doesn't catch the corresponding success callback, in this case Task2 and our catch, only previous errors on the chain (i.e. errors from Task1 or any previous step).

Omitting an error callback means "pass this through unchanged". i.e. same as e => Promise.reject(e) here.

let tag = (e, name) => (e.isFrom = name, Promise.reject(e));

let a = () => Promise.resolve();
let b = () => Promise.reject(new Error("Fail"));
let c = () => Promise.resolve();
let x = () => null.f();

let foo = failFirstStep => Promise.resolve()
.then(failFirstStep? x : a)
.then(() => b().catch(e => tag(e, "b")))
.then(c)
.catch(e => console.log((e.isFrom || "something else") + " failed"));

foo(false).then(() => foo(true));

To generalize, you could tag functions instead, but I find it hard to do in a way that's idiomatic and doesn't interfere with readability or timing, so I'm going to leave that as an exercise. In practice, there's a better answer, but I find this technique interesting enough to cover, and useful in certain cases.




回答5:


You may do like this;

Promise.resolve(21).then(v => v*2)
                   .then(v => {
                                try { throw new Error(v + " is immutable") } // assuming that your code results an error
                                catch(err){ return Promise.reject({message: "2nd then stage threw an error", err:err}) }
                              })
                   .then(v => v)
                   .catch(err => console.log(err.message, "\n",err.err.message));


来源:https://stackoverflow.com/questions/39223854/how-do-i-know-which-handlers-throw-error-in-promise

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!