Promise constructor with reject call vs throwing error
JavascriptPromiseJavascript Problem Overview
In the following code:
var p1 = new Promise(function (resolve, reject) {
throw 'test1';
});
var p2 = new Promise(function (resolve, reject) {
reject('test2');
});
p1.catch(function (err) {
console.log(err); // test1
});
p2.catch(function (err) {
console.log(err); // test2
});
Is there any difference between using reject
(in p2
) from the Promise
api, and throwing an error (in p1
) using throw
?
Its exactly the same?
If its the same, why we need a reject
callback then?
Javascript Solutions
Solution 1 - Javascript
> Is there any difference between using reject
(in p2
) from the Promise
api, and throwing an error (in p1
) using throw
?
Yes, you cannot use throw
asynchronously, while reject
is a callback. For example, some timeout:
new Promise(_, reject) {
setTimeout(reject, 1000);
});
> Its exactly the same?
No, at least not when other code follows your statement. throw
immediately completes the resolver function, while calling reject
continues execution normally - after having "marked" the promise as rejected.
Also, engines might provide different exception debugging information if you throw
error objects.
For your specific example, you are right that p1
and p2
are indistinguishable from the outside.
Solution 2 - Javascript
I know this is a bit late, but I don't really think either of these answers completely answers the questions I had when I found this, Here is a fuller example to play with.
var p1 = new Promise(function (resolve, reject) {
throw 'test 1.1'; //This actually happens
console.log('test 1.1.1'); //This never happens
reject('test 1.2'); //This never happens because throwing an error already rejected the promise
console.log('test 1.3'); //This never happens
});
var p2 = new Promise(function (resolve, reject) {
reject('test 2.1'); //This actually happens
console.log('test 2.1.1'); //This happens BEFORE the Promise is rejected because reject() is a callback
throw 'test 2.2'; //This error is caught and ignored by the Promise
console.log('test 2.3'); //This never happens
});
var p3 = new Promise(function (resolve, reject) {
setTimeout(function() { reject('test 3.1');}, 1000); //This never happens because throwing an error already rejected the promise
throw('test 3.2'); //This actually happens
console.log('test 3.3'); //This never happens
});
var p4 = new Promise(function (resolve, reject) {
throw('test 4.1'); //This actually happens
setTimeout(function() { reject('test 4.2');}, 1000); //This never happens because throwing an error already rejected the promise
console.log('test 4.3'); //This never happens
});
var p5 = new Promise(function (resolve, reject) {
setTimeout(function() { throw('test 5.1');}, 1000); //This throws an Uncaught Error Exception
reject('test 5.2'); //This actually happens
console.log('test 5.3'); //This happens BEFORE the Promise is rejected because reject() is a callback
});
var p6 = new Promise(function (resolve, reject) {
reject('test 6.1'); //This actually happens
setTimeout(function() { throw('test 6.2');}, 1000); //This throws an Uncaught Error Exception
console.log('test 6.3'); //This happens BEFORE the Promise is rejected because reject() is a callback
});
p1.then(function (resolve) {
console.log(resolve, "resolved")
}, function (reject) {
console.log(reject, "rejected")
}).catch(function (err) {
console.log(err, "caught"); // test1
});
p2.then(function (resolve) {
console.log(resolve, "resolved")
}, function (reject) {
console.log(reject, "rejected")
}).catch(function (err) {
console.log(err, "caught"); // test2
});
p3.then(function (resolve) {
console.log(resolve, "resolved")
}, function (reject) {
console.log(reject, "rejected")
}).catch(function (err) {
console.log(err, "caught"); // test3
});
p4.then(function (resolve) {
console.log(resolve, "resolved")
}, function (reject) {
console.log(reject, "rejected")
}).catch(function (err) {
console.log(err, "caught"); // test4
});
p5.then(function (resolve) {
console.log(resolve, "resolved")
}, function (reject) {
console.log(reject, "rejected")
}).catch(function (err) {
console.log(err, "caught"); // test5
});
p6.then(function (resolve) {
console.log(resolve, "resolved")
}, function (reject) {
console.log(reject, "rejected")
}).catch(function (err) {
console.log(err, "caught"); // test6
});
Solution 3 - Javascript
No, there is not, the two are completely identical. The only difference and why we need reject
is when you need to reject asynchronously - for example if you're converting a callback based API it might need to signal an asynchronous error.
var p = new Promise(function(resolve, reject){
someCallbackApi(function(err, data){
if(err) reject(err); // CAN'T THROW HERE, non promise context, async.
else resolve(data);
});
});
Solution 4 - Javascript
A very interesting observation is that if you use throw
it will be handled by first the reject
handler & then theerror
handler if a reject handler is not in place.
With reject handler block
var allowed = false; var p1 = new Promise( function(resolve, reject) { if (allowed) resolve('Success'); else // reject('Not allowed'); throw new Error('I threw an error') })
p1.then(function(fulfilled) { console.log('Inside resolve handler, resolved value: ' + fulfilled); }, function(rejected) { console.log('Inside reject handler, rejected value: ' + rejected); }).catch(function(error) { console.log('Inside error handler, error value: ' + error); })
Without reject handler block
var allowed = false; var p1 = new Promise( function(resolve, reject) { if (allowed) resolve('Success'); else // reject('Not allowed'); throw new Error('I threw an error') })
p1.then(function(fulfilled) { console.log('Inside resolve handler, resolved value: ' + fulfilled); }).catch(function(error) { console.log('Inside error handler, error value: ' + error); })
Additionally, the catch block will be able catch any error thrown inside the resolve
handler.
var allowed = true; var p1 = new Promise( function(resolve, reject) { if (allowed) resolve('Success'); else // reject('Not allowed'); throw new Error('I threw an error') })
p1.then(function(fulfilled) { console.log('Inside resolve handler, resolved value: ' + fulfilled); throw new Error('Error created inside resolve handler block'); }).catch(function(error) { console.log('Inside error handler, error value: ' + error); })
It looks like it's best to use throw
, unless you can't if you are running some async task, you will have to pass the reject
callback down to the async function. But there's a work around, that is is to promisifying your async function. More on https://stackoverflow.com/a/33446005