In the following code, I want to be able to exit app when foo fails, but not when goo fails. My problem is that when goo rejects, it's getting caught in foo's catch. Is there a promise best practice solution to this issue?
foo().then(() => {
return goo.catch(() => {
//do something
//reject but don't exit app
})
})
.catch(() => {
//exit app
//reject
})
Don't reject in goo.catch - you don't want rejection to propagate to the foo.catch handler.
foo().then(() => {
return goo.catch(() => {
// do something but don't reject
})
})
.catch(() => {
// exit app
// reject
})
So you enter catch block and "recover" from error situation. By throwing/rejecting you pass exception further, in case of goo you don't want this.
Simply return from goo.catch() block (or call Promise.resolve())
const gooError;
foo().then(() => {
return goo.catch(err => {
// set the error thrown in to gooError that is
// declared before the promise chain
// and then simply 'return'
gooError = err;
return;
});
// to ensure goo error is taken outside immediately
// You can omit this if not required.
if (gooError) return;
})
.catch(() => {
// exit app
// reject
})
// check if there was any error by goo
if (gooError) {
// handle goo error here
}
I think this does the trick:
foo()
.catch(() => {
//exit app
//reject
})
.then(() => {
return goo.catch(() => {
//do something
//reject
})
})
Let's see what you want to achieve in synchronous code:
function main() {
try {
foo();
} catch(e) {
console.log("Exit process");
throw e;
}
try {
goo();
} catch(e) {
console.log("do something");
throw e;
}
}
You can achieve this by nesting catches:
function foo() {
return Promise.reject();
}
function goo() {
return Promise.reject();
}
function main() {
return foo().catch(e => {
console.log("Exit process");
// what would be point in rejecting if the process already exited anyway?
throw e;
}).then(() => {
return goo().catch(e => {
console.log("Do something");
throw e;
});
});
}
main().catch(() => { console.log("Main is rejected"); });
Related
I'd like to make a function what "wrap" Promise and:
Do something when the promise is invoked (1)
Do something when the promise is resolved, but retain original value (2)
Do something when the promise is rejected but retain the error (3)
I'm not very confident with promises... here is what I may do:
const wrap = (promiseToWrap) => {
return new Promise((resolve, reject) => {
console.log('loading...'); // (1)
promiseToWrap
.then(res => {
console.log('show success toast'); // (2)
resolve(res);
})
.catch(err => {
console.log('show error toast') // (3)
reject(err);
});
});
}
Assuming this solution is right, what's the difference with chaining a new Promise (the one that do "something") with the passed promiseToWrap?
You don't need to create a new promise, you can chain the existing one. To keep the return value and error, just return the value and re-throw it respectively:
const wrap = promiseToWrap => {
console.log('loading...'); // (1)
return promiseToWrap.then(
res => {
console.log('show success toast'); // (2)
return res;
},
err => {
console.log('show error toast') // (3)
throw err;
}
);
}
Better though, write it with async:
const wrap = async (promiseToWrap) => {
console.log('loading...'); // (1)
try {
const res = await promiseToWrap;
console.log('show success toast'); // (2)
return res;
} catch (err) {
console.log('show error toast') // (3)
throw err;
}
}
Alternatively, you can simply "latch onto" the promise to trigger additional callbacks, and return the original promise unchanged:
const wrap = promiseToWrap => {
console.log('loading...'); // (1)
promiseToWrap.then(
res => console.log('show success toast'), // (2)
err => console.log('show error toast') // (3)
);
return promiseToWrap;
}
What you wrote is equivalent to
const wrap = (promiseToWrap) => {
console.log('loading...'); // (1)
return promiseToWrap
.then(res => {
console.log('show success toast'); // (2)
return res;
})
.catch(err => {
console.log('show error toast') // (3)
throw err;
});
}
or more succinctly
const wrap = async (promiseToWrap) => {
console.log('loading...'); // (1)
try {
const result = await promiseToWrap;
console.log('show success toast'); // (2)
return result;
} catch (err) {
console.log('show error toast') // (3)
throw err;
}
};
This probably isn't what you want.
Note that Promise callbacks are immediately invoked, by the time the Promise to wrap is passed to your function it is already "loading" and may well be resolved.
I am currently figuring out how to throw an Exception out of a then catch block. I want to get into the catch that is inside the errorHandler() function.
const errorHandler = function () {
try {
thisFunctionReturnsAnError().then(response => {
console.log(response);
});
} catch (e) {
console.log(e); //How to trigger this?
}
};
const thisFunctionReturnsAnError = function () {
return3()
.then(value => {
throw new Error('This is the error message.');
})
.catch(err => {
//return Promise.reject('this will get rejected');
throw err;
//this one should somehow got to the catch that is located in the errorHandler() function. How to do this?
//I know that this 'err' will be in the next catch block that is written here. This is not what i want.
});
};
const return3 = async function () {
return 3;
};
errorHandler();
I searched a while on stackoverflow but nothing helped me. I am sure that this question got asked often but I could not find the answer, sorry for that.
EDIT:
added here another version of the code but it still does not work
const errorHandler = async function () {
try {
thisFunctionReturnsAnError()
.then(response => console.log(response))
.catch(err => console.log(err));
} catch (e) {
//console.log(e); //How to trigger this?
}
};
const thisFunctionReturnsAnError = function () {
return3()
.then(value => {
throw new Error('This is the error message.');
})
.catch(err => {
return Promise.reject(`Message is: ${err}`);
});
};
const return3 = async function () {
return 3;
};
errorHandler();
I will get the following error message:
Uncaught (in promise) Message is: Error: This is the error message.
Your code can't be executed, because "thisFunctionReturnsAnError" is not returning an Promise. That means that you can't call "then" on the return value.
thisFunctionReturnsAnError().then(response => { // will not work
Why not always use a promise?
const errorHandler = function () {
thisFunctionReturnsAnError()
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log('errorHandler: Handle the error.');
});
};
const thisFunctionReturnsAnError = function () {
return return_Three()
.then((value) => {
throw new Error('This is the error message.');
})
.catch((err) => {
//return Promise.reject('this will get rejected');
throw err;
//this one should somehow got to the catch that is located in the errorHandler() function. How to do this?
//I know that this 'err' will be in the next catch block that is written here. This is not what i want.
});
};
const return_Three = async function () {
return 3;
};
errorHandler();
/*****JUST ANOTHER SYNTAX*******/
const secondErrorHandler = async function () {
try {
await thisFunctionReturnsAnError();
} catch (error) {
console.log('secondErrorHandler: Handle the error.');
}
};
secondErrorHandler();
You cannot handle promise rejections with synchronous try/catch. Don't use it, use the promise .catch() method like in your thisFunctionReturnsAnError function.
You can handle promise rejections with try/catch when using async/await syntax (which you already do, albeit unnecessarily, in return3):
async function errorHandler() { /*
^^^^^ */
try {
const response = await thisFunctionReturnsAnError();
// ^^^^^
console.log(response);
} catch (e) {
console.log(e); // works
}
}
I have a Node.js app. This app has a button that starts a process. The steps in that process return promises. I'm trying to chain these promises together. For some reason, I'm receiving an UnhandledPromiseRejectionWarning. However, in my mind, I've set this up correctly. My code looks like this:
var myButton = document.getElementById('myButton');
if (myButton) {
console.log('here');
myButton.addEventListener('click', executeAction('0'));
}
function executeAction(input) {
let param1 = 'A';
let promise = new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
return promise;
}
function executeStep1() {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
resolve('[something]');
}, 3000);
} catch (ex) {
reject();
}
});
return promise;
}
function executeStep2(p1, p2, p3) {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
console.log('step 2 has executed');
resolve('awesome!')
}, 3000);
} catch (ex) {
reject(ex);
}
});
return promise;
}
I've confirmed that the executeStep2 function runs to completion. I'm basing this in the fact that I can see "step 2 has executed" in the console window. However, to my surprise, I never see "All done" printed in the console window. Instead, I see the UnhandledPromiseRejectionWarning mentioned above. I don't understand two things about this result:
Why am I not seeing "All done" in the console? Shouldn't that function get executed after executeStep2 has resolved?
Where is the rejection coming from? I don't see anything that's rejecting this.
Thank you very much for your help!
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => { // no function(result) here
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
The error is generated from when you call the function(s) that uses promises:
myButton.addEventListener('click', executeAction('0'));
You need to catch rejections there also.
myButton.addEventListener('click', executeAction('0')
.catch((error) => console.log('ERROR', error));
The rejections are caught inside the functions, but not in the outer scope because executeAction('0') returns a promise, or it would but you are using it as a non-async function, so its creating a promise and then returning a pending promise without waiting for it to be resolved. That looks like what's causing the rejection, and its also not handled for the above reason.
This will fix it:
function executeAction(input) {
let param1 = 'A';
return new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
}
You should look into async/await. It can clean this code up significantly.
async function getSomething() {
try {
// throw 'Test error detected.'
return 'test'
}
catch (e) {
throw e
}
}
async function testing() {
try {
const sample = await getSomething()
return sample
} catch (e) {
throw e
}
}
testing()
.then((data) => console.log(data))
.catch((err) => console.log(err))
Run this above example, and then uncomment the throw. Use this pattern for maximum winning. Exceptions should be thrown to the surface level where the functions are called, displayed, used, rendered, etc. Functions should simply throw the error out.
This allows you to use error handling middleware, higher-order functions, listeners, logging, etc.
I am trying to use promises for a small new project. But I have some problems with understanding how I could organize my code better, with functions. Problem is, I want my functions to handle things, and my main code to handle other things. So let's see this code:
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
}).catch(error => {
console.log("in my function",error)
})
}
doSomething().then(response => {
console.log("in my main call", response)
})
With this code, the console will log in my function something and in my main call undefined: https://jsfiddle.net/hkacvw2g/
So I found two solutions to solve my problem, but I just don't like them:
The first one is to create a variable, use a .then() on the variable, and then return the variable (https://jsfiddle.net/hkacvw2g/1/):
function doSomething (isGoingToResolve = true) {
let promise = new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
})
promise.then(response => {
console.log("in my function",response)
}).catch(error => {
console.log("in my function",error)
})
return promise
}
And the second solution (https://jsfiddle.net/hkacvw2g/2/):
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
return new Promise(resolve => {
resolve(response)
})
}).catch(error => {
console.log("in my function",error)
return new Promise((resolve,reject) => {
reject(error)
})
})
}
Am I missing a better solution to solve my problem?
You can simply return the value (or re-throw the error) and it will be resolved in promise chain:
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
return response;
}).catch(error => {
console.log("in my function",error)
throw error;
})
}
You might not want that throw error, it depends on how you want to handle your rejections. This way when you re-throw the error, you should catch it when calling the doSomething() method.
You can write a tap function, to tap into a promise chain, and do something while passing along the result, and a parallel tapCatch function, to tap into errors while rethrowing them:
const tap = fn => value => { fn(value); return value; };
const tapCatch = fn => reason => { fn(reason); throw reason; };
Now you can write your code as:
function doSomething(isGoingToResolve = true) {
(isGoingToResolve ?
Promise.resolve("something") :
Promise.reject("something else")
)
.then( tap (response => console.log("in my function", response)))
.catch(tapCatch(error => console.log("in my function", error)));
}
doSomething()
.then(response => console.log("in my main call", response));
However, actually your first solution is better. It reduces the risk of messing up the promise chain by inadvertently forgetting to, or not realizing that you have to, return the original value in then clauses, or rethrow in catch clauses which such clauses are meant only for logging purposes or other side-effects.
You could also pollute the Promise prototype with something like tap (we'll call it thenDo), making it a bit easier to use:
Promise.prototype.thenDo = function(ifFulfilled, ifRejected) {
return this.then(
value => { ifFulfilled(value); return value; },
reason => { ifRejected(reason); throw reason; });
};
Promise.prototype.catchDo = function(ifRejected) {
return this.catch(reason => { ifRejected(reason); throw reason; });
};
Now you can write
function doSomething(isGoingToResolve = true) {
(isGoingToResolve ?
Promise.resolve("something") :
Promise.reject("something else")
)
.thenDo (response => console.log("in my function", response))
.catchDo(error => console.log("in my function", error));
}
I am using Q.js as a promises library. Previously, theres code like:
function X() {
return Q.Promise(function(resolve, reject) {
Users.find()
.then(function(user) {
Q.all([
getUserProfileInfo(user),
getUserSomethingElse(user)
])
.spread(function(profile, something) {
// do stuff
resolve();
})
.catch(function(err) {
// handle error for Q.all()
reject();
})
})
.catch(function(err) {
// handle error for User.find()
reject();
});
});
}
But now I learnt that I can remove the nesting with something like:
function X() {
return Q.Promise(function(resolve, reject) {
return Users.find()
.then(function(user) {
return Q.all([
getUserProfileInfo(user),
getUserSomethingElse(user)
]);
})
.spread(function(profile, something) {
// do stuff
resolve();
})
.catch(function(err) {
// now how do I differenciate between
// the errors from Users.find() and Q.all()?
reject();
});
});
}
The "problem" I have with the bottom (flattened) version is how do I differenciate the errors from Users.find() and Q.all()? Or in general possibly many other errors in the long then chain?
Currently I might use something like
function(err) {
if (err.errors && err.statusCode) {
// do something
} else if (err.message) {
// do something
}
}
But this is not really "nice" code isit? There must be a better way?
You can use multiple catch blocks, like this
function X() {
return Q.Promise(function(resolve, reject) {
return Users.find()
.catch(function(err) {
// Handle user finding error
throw new Error("Problem in finding users");
})
.then(function(user) {
return Q.all([
getUserProfileInfo(user),
getUserSomethingElse(user)
]);
})
.spread(function(profile, something) {
// do stuff
resolve();
})
.catch(function(err) {
reject();
});
});
}
Now, if there is a problem in finding the user, the first catch block will be executed and since you are throwing an Error from that, it will reach the next catch block in the chain. If there is no error in the Users.find then control will directly go to the then block.