Promise constructor with reject call vs throwing error

JavascriptPromise

Javascript 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlanteView Question on Stackoverflow
Solution 1 - JavascriptBergiView Answer on Stackoverflow
Solution 2 - JavascriptJonathan RysView Answer on Stackoverflow
Solution 3 - JavascriptBenjamin GruenbaumView Answer on Stackoverflow
Solution 4 - JavascriptGavinView Answer on Stackoverflow