Promise constructor with reject call vs throwing error - javascript

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?

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.

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
});

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);
});
});

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

Related

Promise chain continues after rejection

I'm having trouble to properly catch an error/reject in a promise chain.
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
}).then(p2).catch(() => {
console.log("Caught p2");
}).then(p3).catch(() => {
console.log("Caught p3");
}).then(() => {
console.log("Final then");
});
When the promise is rejected, the following .then still gets executed. In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
P1
P2
Caught p2
P3
Final then
The rejection gets caught correctly, but why is "P3" logged after the catch?
What am I doing wrong?
To clarify #evolutionxbox, this is my expected result:
Promise.resolve().then(() => {
console.log("resolve #1");
return Promise.reject();
}).then(() => {
console.log("resolve #2");
return Promise.resolve();
}).then(() => {
console.log("resolve #3");
return Promise.resolve();
}).then(() => {
console.log("Final end");
}).catch(() => {
console.log("Caught");
});
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
Here is a synchronous equivalent of your code:
const f1 = () => {
console.log("F1");
};
const f2 = () => {
console.log("F2");
throw new Error();
};
const f3 = () => {
console.log("F3");
};
try {
f1();
} catch {
console.log("Caught f1");
}
try {
f2();
} catch {
console.log("Caught f2");
}
try {
f3();
} catch {
console.log("Caught f3");
}
console.log("Final code");
As you can see, that gives a matching result. Hopefully, looking at the synchronous code you would not be surprised why. In a try..catch you are allowed to attempt recovery. The idea is that the catch will stop the error propagation and you can hopefully continue further. Or if you do want to stop, you still have to explicitly throw again, for example:
doCode();
try {
makeCoffee();
} catch(err) {
if (err instanceof IAmATeapotError) {
//attempt recovery
makeTea();
} else {
//unrecoverable - log and re-throw
console.error("Fatal coffee related issue encountered", err);
throw err;
}
}
doCode();
This is also the purpose Promise#catch() serves - so you can attempt recovery or at least act when there was a problem. The idea is that after the .catch() you might be able to continue:
const orderPizza = (topping) =>
new Promise((resolve, reject) => {
if (topping === "pepperoni")
reject(new Error("No pepperoni available"));
else
resolve(`${topping} pizza`);
});
const makeToast = () => "toast";
const eat = food => console.log(`eating some ${food}`);
async function main() {
await orderPizza("cheese")
.catch(makeToast)
.then(eat);
console.log("-----");
await orderPizza("pepperoni")
.catch(makeToast)
.then(eat);
}
main();
In order to reject the promise chain from a .catch() you need to do something similar as a normal catch and fail at the error recovery by inducing another error. You can throw or return a rejected promise to that effect.
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
The second piece of code you show fails entirely after a reject because there are no other .catch()-es that are successful. It is basically similar to this synchronous code:
try {
console.log("log #1");
throw new Error();
console.log("log #2");
console.log("log #3");
console.log("Final end");
} catch {
console.log("Caught");
}
Thus if you do not want to recover early, you can also skip the .catch() instead of inducing another error.
Try this.
const p1 = (arg) => {
// Promise returns data in the respected arguments
return new Promise((resolve, reject) => {
// Data to be accessed through first argument.
resolve(arg);
});
};
const p2 = (arg) => {
return new Promise((resolve, reject) => {
// Data to be accessed through second argument.
reject(arg);
});
}
p1('p1').then(resolve => {
console.log(resolve + ' is handled with the resolve argument. So it is accessed with .then()');
}) // Since reject isn't configured to pass any data we don't use .catch()
p2('p2').catch(reject => {
console.log(reject + ' is handled with the reject argument. So it is accessed with .catch()');
}) // Since resolve ins't configured to pass any data we don't use .then()
// You would normally configure a Promise to return a value on with resolve, and access it with .then() when it completes a task successfully.
// .catch() would then be chained on to the end of .then() to handle errors when a task cannot be completed.
// Here is an example.
const p3 = () => {
return new Promise((resolve, reject) => {
var condition = true;
if (condition === true) {
resolve('P3');
} else {
reject('Promise failed!');
}
});
};
p3('p3').then(resolve => {
console.log(resolve);
}).catch(reject => {
console.log(reject);
})
You do not do anything wrong.
In your code you call the first promise p1. Then you write p1.catch(...).then(...).then(...).then(...). This is a chain which means that you should call then 3 times, because you called resolve method in the p1 promise (all these thens depend on the first promise).
When the promise is rejected, the following .then still gets executed.
Yes. Just to be accurate: the then and catch method calls are all executed synchronously (in one go), and so all promises involved are created in one go. It's the callbacks passed to these methods that execute asynchronously, as the relevant promises resolve (fullfill or reject).
In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
This is not the case. The promise that a catch returns can either fullfill or reject depending on what happens in the callback passed to it, and so the callbacks further down the chain will execute accordingly when that promise resolves.
The rejection gets caught correctly, but why is "P3" logged after the catch?
As in your case the catch callback returns undefined (it only performs a console.log), its promise fullfulls! By consequence, the chained then callback -- on that promise -- is executed... etc.
If you want to "stop"
If you want to keep the chain as it is, but wish to have a behaviour where a rejection leads to no further execution of then or catch callbacks, then don't resolve the associated promise:
const stop = new Promise(resolve => null);
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
return stop; // don't resolve
}).then(p2).catch(() => {
console.log("Caught p2");
return stop;
}).then(p3).catch(() => {
console.log("Caught p3");
return stop;
}).then(() => {
console.log("Final then");
});

Handling Js promise rejection

How do you handle an error (eg. "new error" below) that is outside of the promise?
function testError() {
throw new Error("new error") // how to handle this?
var p123 = new Promise(function(resolve, reject) {
resolve(123)
});
return p123
};
testError().catch(err => {
return err; // code doesn't come here
})
.then(ok => {
console.log(ok)
});
If you're not sure whether a function will throw (or return a value) synchronously, you can call it using .then(). This will wrap it so that the result will be handled consistently no matter how it is produced:
function testError() {
throw new Error("new error") // how to handle this?
var p123 = new Promise(function(resolve, reject) {
resolve(123)
});
return p123
};
Promise.resolve()
.then(testError)
.catch(err => {
console.error(err);
return err;
})
.then(ok => {
console.log(ok.message)
});
Since the error doesn't involve the async code, a regular try-catch should do fine here:
try {
testError().catch(err => {
return err; // code doesn't come here
})
.then(ok => {
console.log(ok)
});
}
catch(e) {
//
}
Note that when the async-await pattern finally becomes the native way of resolving promises, the try-catch will also become the native way of handling errors:
try {
var ok = await testError();
console.log(ok)
}
catch(e) {
console.log('e:' +e);
}
As one can easily verify, this one correctly handles both the sync and the async error and is much cleaner than then-catch.
If you can, rewrite your testError function like so
function testError () {
return new Promise(function (resolve, reject) {
throw new Error('new error')
resolve(123)
})
}
testError().then(ok => console.log(ok),
err => console.error(err.message))
Run it once to see it throw the error in console.error
Comment out the throw line to see the promise resolve successfully
Since the error is thrown outside of the promises, you cannot catch it using a promise catch statement.
You can use a try/catch to catch the error instead.
function testError() {
throw new Error("new error") // how to handle this?
var p123 = new Promise(function(resolve, reject) {
resolve(123)
});
return p123
};
try {
testError().then(ok => {
console.log(ok)
});
} catch (err) {
console.log(err.message);
}
You rewrite it, because making a caller check for both exceptions and rejections is an anti-pattern:
function testError() {
return Promise.resolve().then(() => {
throw new Error("new error"); // rejects returned promise
return new Promise(function(resolve) {
resolve(123);
});
});
}
testError().catch(err => console.log("Caught " + err));
This is implicit with async functions; they always return a promise:
async function testError() {
throw new Error("new error"); // rejects implicit promise
return await new Promise(function(resolve) {
resolve(123);
});
}
testError().catch(err => console.log("Caught " + err));

Unable to handle errors in javascript promise

Why doesn't it run the code "console.log(err)" below? but instead returns "TypeError: Cannot create property 'uncaught' on string 'error in promise'"
function abc() {
throw "error in promise";
return 123;
};
abc().catch(function(err) {
console.log(err)
}).then ( abcMessage =>
console.log(abcMessage)
)
.then and .catch require a Promise to be constructed. You are not returning a promise. The promise callback (executor) takes two arguments, a resolver, and a rejecter. Depending on what happens in the code, you may need to call resolve if everything goes right, or reject if something goes wrong.
function abc() {
return new Promise(function(resolve, reject) {
reject(123)
});
};
abc()
.catch(err => {
console.log(err);
return err;
})
.then(abcMessage => {
console.log(abcMessage)
});
new Error("error in promise")

Promises not catching error correctly NodeJS

If I remember correctly Promises are supposed to catch an error when one is thrown at all times so that Promise.catch() can be used to handle that error. I don't recall any exceptions however when I throw an error inside setTimeout() this somehow doesn't work.
Can someone explain why this doesn't work? Or is it simply a bug in NodeJS?
Test code
// This works!
function async() {
return new Promise(function (resolve, reject) {
throw new Error('test');
});
}
async().catch(function() {
console.log('Ok: 1');
});
// This doesn't work..
function async_fail() {
return new Promise(function (resolve, reject) {
setTimeout(function() {
throw new Error('test');
}, 1);
});
}
async_fail().catch(function() {
console.log('Ok: 2');
});
You will never catch an Error that is thrown in setTimeout, because it executes async to the actual execution of the promise function. So the promise itself already finished (without any call to resolve or reject) when the function inside set timeout is called.
If you want the promise to fail based on an error inside setTimeout you will need to catch it manually and call reject:
setTimeout(function() {
try{
throw new Error('test');
}catch(ex){
reject(ex);
}
}, 1);

Why does my Promise fall with error? [duplicate]

What is the best way to handle this scenario. I am in a controlled environment and I don't want to crash.
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
When throwing from within the setTimeout we will always get:
$ node bluebird.js
c:\blp\rplus\bbcode\scratchboard\bluebird.js:6
throw new Error("AJAJAJA");
^
Error: AJAJAJA
at null._onTimeout (c:\blp\rplus\bbcode\scratchboard\bluebird.js:6:23)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
If the throw occurs before the setTimeout then bluebirds catch will pick it up:
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
throw new Error("Oh no!");
setTimeout(function(){
console.log("hihihihi")
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
Results in:
$ node bluebird.js
Error [Error: Oh no!]
Which is great - but how would one handle a rogue async callback of this nature in node or the browser.
Promises are not domains, they will not catch exceptions from asynchronous callbacks. You just can't do that.
Promises do however catch exceptions that are thrown from within a then / catch / Promise constructor callback. So use
function getPromise(){
return new Promise(function(done, reject){
setTimeout(done, 500);
}).then(function() {
console.log("hihihihi");
throw new Error("Oh no!");
});
}
(or just Promise.delay) to get the desired behaviour. Never throw in custom (non-promise) async callbacks, always reject the surrounding promise. Use try-catch if it really needs to be.
After dealing with the same scenario and needs you are describing, i've discovered zone.js , an amazing javascript library , used in multiple frameworks (Angular is one of them), that allows us to handle those scenarios in a very elegant way.
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for JavaScript VMs
Using your example code :
import 'zone.js'
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
Zone.current
.fork({
name: 'your-zone-name',
onHandleError: function(parent, current, target, error) {
// handle the error
console.log(error.message) // --> 'AJAJAJA'
// and return false to prevent it to be re-thrown
return false
}
})
.runGuarded(async () => {
await getPromise()
})
Thank #Bergi. Now i know promise does not catch error in async callback. Here is my 3 examples i have tested.
Note: After call reject, function will continue running.
Example 1: reject, then throw error in promise constructor callback
Example 2: reject, then throw error in setTimeout async callback
Example 3: reject, then return in setTimeout async callback to avoid crashing
// Caught
// only error 1 is sent
// error 2 is reached but not send reject again
new Promise((resolve, reject) => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Nothing happen
})
.then(() => {})
.catch(err => {
console.log("Error", err);
});
// Uncaught
// error due to throw new Error() in setTimeout async callback
// solution: return after reject
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Did run and cause Uncaught error
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});
// Caught
// Only error 1 is sent
// error 2 cannot be reached but can cause potential uncaught error if err = null
new Promise((resolve, reject) => {
setTimeout(() => {
const err = "error 1";
if (err) {
reject(err); // Send reject
console.log("Continue"); // Did print
return;
}
throw new Error("error 2"); // Potential Uncaught error if err = null
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});

Categories

Resources