Throw inside a callback inside a promise [duplicate] - javascript

This question already has an answer here:
Using Q.promises: how to catch an async throw?
(1 answer)
Closed 6 years ago.
I know that stackoverflow is full of similar question and I've read a lot of them.
From what I got a throw inside a promise should reject it, as I can read in the documentation:
If the executor throws an exception, its value will be passed to the reject resolving function.
But even after read a lot of post about promises and throw I still don't understand the snippet of code I'm pasting and why it happens.
function foo(a, b, cb) {
setTimeout(() => {
cb('Inner error *!?"$%&##"');
}, 0);
}
const getThePromise = () => {
return new Promise((resolve, reject) => {
const cb = (err) => {
/* >>> ************ */
throw err; // catch not called
// reject(err); // catch called
/* ************ <<< */
}
foo('foo', 'dudee', cb);
});
}
getThePromise()
.catch((err) => {
console.log('CATCH:', err);
})
.then((res) => {
console.log('then...');
})
I don't understand why if I use the throw the .catch of the promise is not called but if I use the reject it is called.
Just for sake of clarification I'm using Node.js v6.2.2 in a Mac OS/X 10.11 but I don't think it could be also a browser issue.

You are throwing your error inside an asynchronous setTimeout call, which will lead to an uncaught error. The asynchronous code will not execute in the same context as the try-catch block. This has nothing to do with the promise API. This is just part of the behavior of asynchronous code execution in JavaScript.
Take a look at the following example.
const asyncOperation = err => {
try {
setTimeout(function() {
throw err; // will be dropped onto the event queue
// until the call stack is empty
// even if this takes longer than
// a second.
}, 1000);
} catch (e) {
console.log(e) // will not be called
}
}
asyncOperation('Inner error *!?"$%&##"')
And now the same example with the try-catch block inside the setTimeout call and the error being thrown inside the try block.
const asyncOperation = err => {
setTimeout(function() {
try {
throw err // here the error will be throw inside
} catch (e) { // the try block and has the same execution
console.log(e) // context.
}
}, 1000);
}
asyncOperation('Inner error *!?"$%&##"')
You can find more information regarding the Promise.catch right here.
Promise.prototype.catch()
The catch() method returns a Promise and deals with rejected cases only.
There is actually an example with the same situation you are describing in your example. Check out
Gotchas when throwing errors
// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
p2.catch(function(e) {
console.log(e); // This is never called
});

Related

Creating reusable promises without .catch

I am working in a new codebase that has a pattern where some reusable functions return a promise from a chain, but they don't handle errors.
Here is an example:
function createScheduleForGroup(group) {
if (notInProduction()) {
group.gschedScheduleId = '0';
return Bluebird.resolve(group);
}
// note: schedule is a global within the file
return schedule.createSchedule(group.name).then(function (schedule) {
group.gschedScheduleId = schedule.id;
return group.save();
});
}
In the example above, there isn't a .catch or a reject function passed to the .then.
The function is eventually used in an express route where the error is handled:
router.post('/schedule', function(req, res, next) {
scheduleLogical
.createScheduleGroup(req[config.entity])
.then(function(group) {
res.status(201).json(group);
})
.catch(next);
// if creteScheduleGroup throws an error it is handled here
Is it a common pattern to not define error handlers for a promise returned from a function, and anticipate whoever uses the function to attach the appropriate error handlers?
To make this clearer for my own understanding, I made a simulation of this specific function and all the functions used within the promise chain. Here it is:
function getScheduleMock() {
// this promise promisifys an older api that uses callbacks
return new Promise((resolve, reject) => {
// note this is simulating an api call:
const response = Math.round(Math.random());
// 0 === err, 1 === success
if(response === 0) return reject(response);
resolve(response);
})
// error hanlding :)
.catch(err => {
console.log(err);
return Promise.reject(err);
// there is an error handling function that logs the errors. If the error doesn't match expected errors, it rejects the error again as I have here.
})
.then(responseData => {
return Promise.resolve(responseData);
})
}
function createScheduleForGroupMock() {
return getScheduleMock().then(responseData => Promise.resolve('everything worked :)'));
// Note: group.save() from the original returns a promise
// just like the actual example, the promise is returned
// but there isn't any form of error handling except within the getScheduleMock function
}
createScheduleForGroupMock(); // when the error is rejected in the .catch() in getScheduleMock, the error is unhandled.
/* ***************** */
/* the createScheduleForGroup method is used within an express route which has error handling, here is an example of the code: */
// router.post('/schedule', function(req, res, next) {
// scheduleLogical
// .createScheduleGroup(req[config.entity])
// .then(function(group) {
// res.status(201).json(group);
// })
// .catch(next);
// if creteScheduleGroup throws an error it is handled here
I'm fairly new to error handling promises and from what I've been reading and practicing, I generally felt you should always include an error handler. The codebase I'm working in has a lot of methods that use the pattern of createScheduleForGroup where there isn't an error handler defined in the function, but instead, it is handled and attached after the function is used.
Some of the functions used within createScheduleForGroup handle their own errors, and I'm confused and curious about the balance on when to handle errors in a function that returns a promise and when to attach them when you use the function.
Is it a common pattern to not define error handlers for a promise returned from a function, and expect whoever uses the function to attach the appropriate error handlers?
Yes, totally. It's not just "a common pattern", it's the absolute standard pattern.
Just like you don't put a try/catch statement in every synchronous function, you don't put a .catch callback on every promise that you return. In fact, it's considered an antipattern to catch errors that you cannot handle.
You can have an error handler in each function.
function aPromise() {
return new Promise(function(resolver, reject){
//Handle any error here and attach different information via your own errror class
})
}
async function parentProcess() {
try{
await aPromise()
}
catch(e) {
//Handle and attach more info here
}
}
function grandParentProcess() {
try{
parentProcess();
}
catch(e){
//Handle the error
}
}
You don't essentially have to handle error in the parent function if the grand parent function handles it to avoid the UnhandledPromiseRejection error.

What is the correct error handling pattern for the calling function when calling a promise? [duplicate]

This question already has answers here:
correct way to handle errors inside a Promise
(3 answers)
Promise constructor with reject call vs throwing error
(4 answers)
Correct Try...Catch Syntax Using Async/Await
(6 answers)
Closed 4 years ago.
I am completely new to Promises and async/await and just reading through tutorials etc., so I expect I may be missing something fundamental.
I've see this code pattern in several places, but I'm thinking maybe it should not be trusted:
async function myFunc() {
try {
var result = await doSomethingAsync();
console.log(result);
}
catch(error) {
console.error(error); // hmm
}
}
myFunc();
Consider the following scenario using the above pattern:
const doSomethingAsync = behaviour => new Promise((resolve, reject) => {
// use setTimeout to simulate e.g. an async webapi callback func
setTimeout(() => {
if(behaviour === "set resolve")
resolve("resolved");
if(behaviour === "set reject")
reject("rejected");
if(behaviour === "set after timeout error")
throw new Error("something went wrong after setTimeout returned");
}, 500);
if(behaviour === "set promise error")
throw new Error("something went wrong in promise");
});
async function myFunc(behaviour) {
try {
// wait for promise to complete
var result = await doSomethingAsync(behaviour);
console.log(result);
}
catch(error) {
console.error("caught:" + error); // will catch reject and promise error,
// but NOT after setTimeout error
}
}
myFunc("set promise error"); // caught:Error: something went wrong in promise
myFunc("set resolve"); // resolved
myFunc("set reject"); // caught:rejected
myFunc("set after timeout error"); // Uncaught Error: something went
// wrong after setTimeout returned
// UHOH!!
So the pattern seems a little misleading as it cannot catch the setTimeout error, whereas people tend to think of catch() as a catch-all.
Of course, changing the setTimeout to have an internal catch will solve the uncaught error problem:
const doSomethingAsync = behaviour => new Promise((resolve, reject) => {
// use setTimeout to simulate e.g. an async webapi call
setTimeout(() => {
try { // ...
}
catch(e) { reject("an error"); }
}, 500);
});
So, my questions are:
As I understand it, Promises are used as a replacement for callbacks, i.e. for wrapping async calls like setTimeout. So should there always be an internal try/catch -> reject pattern implemented inside a Promise? (So that the error can be gracefully handled by the caller). Do e.g. all node libraries work in this way?
I can't see any way for the caller to deal with it by itself.
I find the pattern misleading, wouldn't the pattern be better like this?
async function myFunc(behaviour) {
try {
var result = await doSomethingAsync(behaviour);
.catch(e) { console.log("expected reject happened:" + e) };
}
catch(error) {
console.error("something totally unexpected happened:" + error);
}
}

How can I catch asynchronous-non-promised errors ? ( react to that specific error)

I know that there are answers out there but I didn't find a specific answer to my actual question.
Currently I use the following pattern a lot :
class A
{
getLocation()
{
return Promise.reject(2222222)
}
async a()
{
try
{
var loc = await this.getLocation();
alert(loc)
}
catch (e)
{
alert("catch 2")
}
}
}
new A().a();
Result : "catch 2"
Event If I throw an error in getLocation :
getLocation()
{
throw Error("ffffff")
}
- I get the same result - which is OK.
So where is the problem ?
Well as you know , an error which is thrown asynchronously-non-promised is a different beast :
So this code won't be catched at all:
getLocation() //bad code from a third party code , third party code
{
return new Promise((v, x) => setTimeout(() =>
{
throw Error("ffffff")
}, 100))
}
Question :
Regarding the fact that I want to catch errors - is there a better pattern for capturing this ?
Sure I can do :
window.onerror = function () { alert(4)}
But that would be not in order as the flow of .catch(...) or catch(){} , and I won't be able to do actions regarding that specific action that caused error.
Full disclosure:
No real life scenario. Learning purpose .
an error which is thrown asynchronously-non-promised is a different beast
Yes. And it must be avoided at all costs. Therefore, never put business code (including trivial things like property accesses) in asynchronous non-promise callbacks. It could throw! It should be obvious that JSON.parse can fail, that a property access can throw when the "object" is null or undefined or a getter is involved, or that a loop can fail when the thing that was supposed to be an array has no .length.
The only things that are allowed as asynchronous non-promise callbacks are resolve, reject, and (err, res) => { if (err) reject(err); else resolve(res); } (and maybe a variadic version for weird APIs with multiple arguments).
So rewrite the bad code to
async getLocation() {
await new Promise(resolve => setTimeout(resolve, 100));
throw Error("ffffff");
}
or
getLocation() {
return new Promise(resolve => setTimeout(resolve, 100)).then(res => {
throw Error("ffffff");
});
}
and when it's third-party code make them fix it, make an upstream merge request of your fix, or if those don't work abandon that party.
is there a better pattern for capturing this?
Well, domains (in node) were supposed to solve this problem of non-locality of asynchronous (uncaught) exceptions, but they didn't work out. Maybe some day, zones with better native language support will replace them.
The errors should be caught in place where they occur.
This kind of code code is incorrect and should be fixed in-place:
getLocation() //bad code from a third party code
{
return new Promise((v, x) => setTimeout(() =>
{
throw Error("ffffff")
}, 100))
}
If this is third-party code, it can be forked or patched.
Exceptions can be tracked globally by onerror, as the question already mentions. This should be used only to notify a developer of existing errors, not to handle them in normal way.
unhandledrejection event can be used for same purpose to notify about unhandled rejections in promises. It won't be able to handle the error in the snipped above because it is thrown inside setTimeout callback and doesn't result in promise rejection.
I guess the basic usage would be like this:
class A {
getLocation(x) {
return new Promise((resolve, reject) => setTimeout(() => {
// a lot of async code
try {
//simulate unexpected exception
if (x === 2) {
throw ("code error");
}
if (x) {
resolve('success');
} else {
reject('conditional rejection');
}
} catch (ex) {
reject(ex);
}
}, 1000));
}
async a(x) {
await this.getLocation(x).then((loc) => console.info(loc)).catch((e) => console.error(e));
}
}
let o = new A();
o.a(2);
o.a(0);
o.a(1);
The rejection of the Promise is not necessarily an code Exception as well as the code Exception should not necessarily trigger a Promise rejection.

Catching errors in ES6 promises

Does anybody have any thoughts about ES6 promises, I'm using them in my Node app and I love them, on the most part. But Ive found that if I get some sort of error in a resolve callback, it won't throw an error or execute the reject callback, and its leaving me with my server hanging infinitely.
For now I've resorted to doing this, and manually rejecting the promise with the caught error, but I'm not sure if this a great way to handle, and/or if I should be using promises at all.
this.dataStore.set(newID, value).then( (foo) => {
try{
this.var = foo;
res({val: foo});
}catch(e){
rej(e);
}
}, (e) => {
rej(e);
});
I think the confusion is arising from the fact that, based on your use of res, and rej here, you are likely calling this from within a promise constructor, along the lines of
function setStore(newID, value) {
return new Promise(function(res, rej) {
this.dataStore.set(newID, value).then( (foo) => {
try{
this.var = foo;
res({val: foo});
}catch(e){
rej(e);
}
}, (e) => {
rej(e);
});
});
}
By the way, the (e) => { rej(e); } part at the end could be rewritten as e => rej(e), which in turn could be rewritten as rej.
But anyway, you don't need any of that surrounding machinery to create and return your own promise, because this.dataStore.set and/or the ensuing call to then already creates a promise, which you can return as is. Instead of creating your own new promise, and then resolving your new promise with the little hash based on the result passed to then, just return the hash--that will become the value of the resulting promise. Instead of rejecting your new promise when the call to dataStore.set fails, just let the failed promise be itself.
So you shouldn't need to do anything more complicated than
function setStore(newID, value) {
return this.dataStore.set(newID, value).then(foo => {
this.var = foo;
return {val: foo};
});
}
An error occurring in the this.var = foo; return {val: foo}; part (but how could it?) will automatically throw the promise into failure state. A failure resulting from this.dataStore.set will yield a failed promise as is, and there is no need to catch it and rethrow it--the failure will progress merrily down the chain.
Use this as:
setStore('abc', 123)
.then(hash => console.log(hash.val))
.catch(e => console.log("The sky is falling", e));
As a matter of clarification, in the following:
promise.then(success, failure)
an error arising in the success callback is not handled in the failure callback. A failure in the success callback would be handled in successive stages of the chain. You could handle a failure in success (or promise itself) with
promise.then(success).catch(failure)

Catching exceptions in setInterval

Quick question, if I do this:
setInterval(function() {
try {
riskyFunc();
} catch(e){
console.log(e);
}
}, 1000);
In my head I am thinking that if anything goes wrong in riskyFunc(), it will be caught. Is this true? There are also some async calls I know of for sure inside riskyFunc().
Yes, it will be caught: but only when the callback is executed. That is, if riskyFunc throws an exception, it won't be caught in your example until the callback executes in one second.
You've probably heard before that you have to be careful with exceptions when using asynchronous methods, and the usual mistake people make is this:
try {
setInterval(function() {
riskyFunc();
}, 1000);
} catch(e) {
console.error(e);
}
They are confused when riskyFunc throws an exception and it isn't caught. It isn't caught because the exception doesn't happen when you call setInterval; it happens when setInterval invokes the anonymous function sometime in the future, which is outside of the context of the original try/catch block. You are doing it the correct way: by doing the exception handling inside the callback.
If riskyFunc in turn invokes asynchronous calls, those too have to handle exceptions in this manner. For example:
function riskyFunc() {
// do some stuff
asyncFn(function(){
throw new Error('argh');
}
}
That exception will not get caught in the try/catch block inside your setInterval call. You'll have to keep applying the pattern on down:
function riskyFunc() {
// do some stuff
asyncFn(function() {
try {
// work that may throw exception
} catch(e) {
console.error(e);
}
}
}
If you want the exception to "propagate up", you'll have to use promises, or some other way to indicate success/failure. Here's a common method, by using a "done" callback that is capable of reporting an error:
function riskyFunc(done) {
// do some stuff
asyncFn(function() {
try {
// do some more risky work
done(null, 'all done!');
} catch(e) {
done(e);
}
}
}
Then you can call that in your setTimeout and take into account possible asynchronous failures:
setTimeout(function() {
try {
riskyFunc(function(err, msg) {
// this will cover any asynchronous errors generated by
// riskyFunc
if(err) return console.error(err);
console.log(msg);
});
} catch(e) {
// riskyFunc threw an exception (not something it
// invoked asynchronously)
console.error(e);
}
}
setInterval already puts the block in an asynchronous block. And exceptions can't be caught in out-of-sync. The right pattern is to use a Promise object, and call a fail() operation if something goes wrong. For this example, I'm using jQuery's Deferred object, but any promise library has similar usage:
var promise = $.Deferred();
setInterval(function () {
try{
riskyFunc();
} catch (e) {
promise.reject(e);
}
promise.resolve(/* some data */);
}, 1000);
promise
.done(function (data) { /* Handled resolve data */ })
.fail(function (error) { /* Handle error */ });
Note that since you're using setInterval instead of setTimeout that this will be called every second unless you clear the timeout, so if you need to call the function multiple times in parallel, you might want an array of promises.
If riskyFunc is
function() {
process.nextTick(function() {
throw "mistake";
});
}
your catch block will not catch. I believe this is the case you are worried about, and all you can do is set global exception handlers, or hope for the best. (No, promises will not catch this. They are not magic.)
Thanks #Ethan Brown for the detailed explanation. I think your last setTimeout is missing the timer - see below
setTimeout(function() {
try {
riskyFunc(function(err, msg) {
// this will cover any asynchronous errors generated by
// riskyFunc
if(err) return console.error(err);
console.log(msg);
});
} catch(e) {
// riskyFunc threw an exception (not something it
// invoked asynchronously)
console.error(e);
}
}, 1000)

Categories