Skip to content Skip to sidebar Skip to footer

How Do I Know Which Handlers Throw Error In Promise?

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

Solution 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:

varPromise = require('bluebird');

// You can input any argument in this function to test workflow of promise functiontestPromise(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 functionsfunctionmakeTask(task) {
    returnfunctiontask1Handler(input) {
        if (input === task) {
            thrownewError(task)
        }            
        return input
    }
}

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

// hint the error when the error has matchedfunctiontaskLog(msg) {
    returnfunctionthelog(err) {
        console.log('It\'s ' + msg)
    }  
}

The example:

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

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

>testPromise('Task3')Task1:falseTask2:falseTask3: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.

Solution 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
}

Solution 3:

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

leta = () => Promise.resolve();
letb = () => Promise.reject(newError("Fail"));
letc = () => 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.

Solution 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:

vartag = (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.

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

leta = () => Promise.resolve();
letb = () => Promise.reject(newError("Fail"));
letc = () => Promise.resolve();
letx = () => null.f();

letfoo = 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.

Solution 5:

You may do like this;

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

Post a Comment for "How Do I Know Which Handlers Throw Error In Promise?"