JavaScript Promises - reject vs. throw
There is no advantage of using one vs the other, but, there is a specific case where throw
won't work. However, those cases can be fixed.
Any time you are inside of a promise callback, you can use throw
. However, if you're in any other asynchronous callback, you must use reject
.
For example, this won't trigger the catch:
new Promise(function() { setTimeout(function() { throw 'or nah'; // return Promise.reject('or nah'); also won't work }, 1000);}).catch(function(e) { console.log(e); // doesn't happen});
Instead you're left with an unresolved promise and an uncaught exception. That is a case where you would want to instead use reject
. However, you could fix this in two ways.
- by using the original Promise's reject function inside the timeout:
new Promise(function(resolve, reject) { setTimeout(function() { reject('or nah'); }, 1000);}).catch(function(e) { console.log(e); // works!});
- by promisifying the timeout:
function timeout(duration) { // Thanks joews return new Promise(function(resolve) { setTimeout(resolve, duration); });}timeout(1000).then(function() { throw 'worky!'; // return Promise.reject('worky'); also works}).catch(function(e) { console.log(e); // 'worky!'});
Another important fact is that reject()
DOES NOT terminate control flow like a return
statement does. In contrast throw
does terminate control flow.
Example:
new Promise((resolve, reject) => { throw "err"; console.log("NEVER REACHED");}).then(() => console.log("RESOLVED")).catch(() => console.log("REJECTED"));
vs
new Promise((resolve, reject) => { reject(); // resolve() behaves similarly console.log("ALWAYS REACHED"); // "REJECTED" will print AFTER this}).then(() => console.log("RESOLVED")).catch(() => console.log("REJECTED"));
Yes, the biggest difference is that reject is a callback function that gets carried out after the promise is rejected, whereas throw cannot be used asynchronously. If you chose to use reject, your code will continue to run normally in asynchronous fashion whereas throw will prioritize completing the resolver function (this function will run immediately).
An example I've seen that helped clarify the issue for me was that you could set a Timeout function with reject, for example:
new Promise((resolve, reject) => { setTimeout(()=>{reject('err msg');console.log('finished')}, 1000); return resolve('ret val')}).then((o) => console.log("RESOLVED", o)).catch((o) => console.log("REJECTED", o));
The above could would not be possible to write with throw.
try{ new Promise((resolve, reject) => { setTimeout(()=>{throw new Error('err msg')}, 1000); return resolve('ret val') }) .then((o) => console.log("RESOLVED", o)) .catch((o) => console.log("REJECTED", o));}catch(o){ console.log("IGNORED", o)}
In the OP's small example the difference in indistinguishable but when dealing with more complicated asynchronous concept the difference between the two can be drastic.