Always Throw Errors In Order To Get A Stack Trace In Promise Chains
The other day, I was developing a Promise-based workflow when I ran into a little dilemma. I had a situation in which I needed to raise an exception. But, the path forward, in the heat of the moment, was suddenly unclear; since I was already inside a Promise chain, should I throw the error or reject the error? Luckily, I didn't have to think about this too deeply because IE (Internet Explorer) forced me down the right path. In IE (both 10 and 11), the .stack property of the Error object isn't actually populated until the Error object is thrown. As such, you must throw the Error object in order get a proper stack trace in a Promise Chain (or anywhere else, for that matter).
To see this in action, I put together a tiny demo that rejects a Promise in two different ways: first, with the reject() function and second, with the throw() operation:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>
Always Throw Errors In Order To Get A Stack Trace In Promise Chains
</title>
</head>
<body>
<h1>
Always Throw Errors In Order To Get A Stack Trace In Promise Chains
</h1>
<p>
<em>Look at console — things being logged, yo!</em>
</p>
<script type="text/javascript" src="../../vendor/core-js/2.4.1/shim.min.js"></script>
<script type="text/javascript">
var promise = new Promise(
function( resolve, reject ) {
// If we REJECT instead of THROW ....
reject( new Error( "Something went wrong!" ) );
}
);
promise.catch(
function handleReject( error ) {
console.warn( "Error:" );
console.log( error.message );
// CAUTION: In IE 10 / 11, the stack property will be undefined because
// the original Error() object was never thrown. IE only populates the
// stack at the time the Error is thrown.
console.log( error.stack );
}
);
// --------------------------------------------------------------------------- //
// --------------------------------------------------------------------------- //
var promise = new Promise(
function( resolve, reject ) {
// If we THROW instead of REJECT ....
throw( new Error( "Something went wrong (but better)!" ) );
}
);
promise.catch(
function handleReject( error ) {
console.warn( "Error:" );
console.log( error.message );
// This time, since we threw the error, instead of simply rejecting it,
// IE 10 and 11 will populate the stack trace.
console.log( error.stack );
}
);
</script>
</body>
</html>
As you can see, both approaches use the core Promise constructor to initiate the Promise chain. The first fulfills the Promise with the explicit rejection of the Error object. The second throws the Error which will implicitly fulfill the Promise with a rejection of the thrown Error object. In Microsoft Edge and all other modern browsers, the two approaches result in the same output; but, in IE 10 and 11, only the latter provides a stack trace:
As you can see, only a thrown Error provides a stack trace in IE 10 and 11.
In hindsight, my confusion seems silly. Of course you throw() Error objects - that's the whole point of creating an Error object. But, at the time, with the reject() function right there, I suddenly felt unsure of myself. Thankfully, IE was there to steer me in the right direction (and how many times do you get to say that in your life!?).
Want to use code from this post? Check out the license.
Reader Comments