问题
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