I have this async block:
test().then(function(result){
// Success: Do something.
doSomething();
}).catch(function(error){
// Error: Handle the error, retry!
// How to re-run this whole block?
});
I can keep track of the success and failed outcomes. However, is it possible to retry the whole test().then().catch() chain if we fail? And keep retrying until the condition resolves?
If you can switch to async/await syntax, you can use a while loop:
let keepTrying;
do {
try {
await test();
keepTrying = false;
} catch {
keepTrying = true;
}
} while (keepTrying)
doSomething();
You could then abstract the retrying logic into its own function to be reused.
Assuming it's all about resending request to some buggy/bloat-up 3rd party API
If it's production question rather educational one I'd suggest search for 3rd party lib that implementing this on your own.
Say for axios there is nice axios-retry.
Why? Assume you may think there is just one case when API say returns 502. But actually there are much more cases it'd be better to keep in mind:
differing particular error causes, say once there is Network or DNS Lookup Error there may be no need to repeat request
retry count limitation
increasing delay
something else
Writing such a logic on your own would be real overkill. And trying to use simplest solution may hit you when you don't expect it.
PS also as a bonus you would be able to configure all requests to some specific API with single snippet like it goes for axios' custom instances(and I believe there should other plugins for alternative libraries)
You could put the whole thing into a function that recursively calls itself if the catch block is entered:
function tryTest() {
return test().then(function(result) {
// Success: Do something.
doSomething();
}).catch(function(error) {
// error handling
// make sure to return here,
// so that the initial call of tryTest can know when the whole operation was successful
return tryTest();
});
}
tryTest()
.then(() => {
console.log('Finished successfully');
});
If your doSomething can take the result argument, and if tryTest doesn't take any arguments, you can simplify the above to:
function tryTest() {
return test()
.then(doSomething)
.catch(tryTest);
}
tryTest()
.then(() => {
console.log('Finished successfully');
});
You can put it in a function.
function dbug() {
test().then(function(result){
// Success: Do something.
doSomething();
}).catch(function(error){
// Error: Handle the error, retry!
dbug()
});
}
Related
I'm still learning the language, and I'm very curious to know what is the proper way to ensure that either all functions will execute or none when one action requires series of functions to be executed. For example I might have an HTML button that calls some apply() function:
function apply() {
try {
// Check arguments, choose what exactly to do next through some IFs etc...
}
anotherFunction();
}
function anotherFunction() {
try {
// Request data from DB, process data received, update object variables, etc...
}
yetAnotherFunction();
}
function yetAnotherFunction() {
try {
// Update HTML
}
oneMoreFunction();
}
function oneMoreFunction() {
try {
// Update graph
}
}
So the problem here is that if any of the functions in the flow throws an error the rest functions won't do what they should, hence the entire Apply process will be interrupted with some changes applied (let's say HTML is getting updated) but the rest (the graph) is not. I'm curious to know what is the best practice to prevent this behaviour? Yes I'm trying my best to use try {} and check arguments for errors etc, but it looks like I can't foresee everything, I just need some way to tell the code "ensure you can execute all of the functions, in case of any errors, just don't do anything at all". Please advise what can be done here?
You're taking the right path when thinking about try/catch blocks, but notice I used a 'catch' as well. Usually (maybe that is even enforced, I can't remember) you need the catch blocks along with the try.
So your functions could look something like this:
function async myFirstTryCatch() {
try {
// Make your request in the try block
await requestCall();
} catch(error){
// Hey, my http call returned an error
// Deal with the error here. Maybe show a toast, validate a form
// Anything you need to not break the code and have good UX
console.log(error)
}
}
In that same line of thought, you could have each function handle their own try/catch or maybe control that in your apply function, in case some of the chain must continue/stop depending each other.
function apply() {
try {
firstCall();
functionThatRequiresFirstCalltoSucceed();
} catch (error){
//Will catch if either firstCall or functionThatRequiresFirstCalltoSucceed fail
console.log(error)
}
functionThatIndependsFromTheResultAbove();
}
I hope this will help you build your thoughts about error handling in JS :)
IMPORTANT NOTE
If your code enters the catch block, it will consider that the error has been dealt with and will not propagate! Here's an example
function functionThatThrowsError(){
try{
throw new Error('Example Error!');
} catch (error) {
// Error has been dealt with
console.log(error) // Returns "Example Error"
// throw error; <--- Throw the error in the catch block if you need t to propagate
}
}
function wontCatchError() {
try {
functionThatThrowsError();
} catch (error) {
// THE CODE WILL NOT ENTER THE CATCH BLOCK
// SINCE THE ERROR WAS CAUGHT IN THE FUNCTION ITSELF.
// If you need to catch here as well, make sure to throw the error
// in the catch block of the 'functionThatThrowsError'
console.log(error)
}
}
I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();
I am attempting to use an async/await pattern in order to handle a scenario that might be considered "callback hell" if implemented otherwise.
Here is an extremely dumbed down version of the code. The real code has about 5 conditional HttpClient calls based on the data from the first call (not my api...) which is the reason why I am using the async/await pattern in the first place.
async blah(): Promise<boolean> {
try {
let resp = await this.http.get("https://httpstat.us/500").toPromise();
console.warn("you should not see this");
// the real code will logically call the api multiple times based on conditonal data from resp
// hence the attempted usage of async/await to avoid "callback hell"
// blah() will eventually return an object.
return true;
}
catch (err) {
console.error("caught inside blah()");
throw err;
}
}
ionViewDidLoad() {
this.blah().then(data => {
console.warn('okokokok');
}).catch(error => {
console.error(error)
});
}
What happens, I can see the call actually 500, but the code continues and the following is printed to the console:
polyfills.js:3 GET https://httpstat.us/500/ 500 (Internal Server Error)
main.js:927 you should not see this
main.js:940 okokokok
As you can see, it isn't catching the 500 (or any other http status I have tested with)
The device I am testing with is a Pixel 2 running P and the console data is coming from a Chrome device inspector session.
Any advice would be greatly appreciated.
** Edit: This is clearly an issue with the combination of ionic and angular... It should work...
** Edit: it turns out to 100% be an Angular issue... Not the framework itself but how an interceptor was implemented. I will leave this here instead of deleting the question in the rare case someone else requires it.
If i uderstood your question correctly, you want to do cascade calls, so you make the http request and based on the response you want to do another http call. If that is the case, then you should consider using switchMap operator:
this.http.get("https://httpstat.us/500").pipe(
switchMap( result => {
if(result.a === 5) {
return this.http.get("some server api url");
}
return return this.http.get("another server api url");
})
)
You handle the errors then in rxjs way.
See cascading calls
Background
I have a REST API using MongoDB, Node.js and Express that makes a request to my NoSQL DB and depending on different results, I want to differentiate which error I send the customer.
Problem
The current version of my code has a generic error handler and always sends the same error message to the client:
api.post("/Surveys/", (req, res) => {
const surveyJSON = req.body;
const sender = replyFactory(res);
Survey.findOne({_id: surveyJSON.id})
.then(doc => {
if(doc !== null)
throw {reason: "ObjectRepeated"};
//do stuff
return new Survey(surveyJSON).save();
})
.then(() => sender.replySuccess("Object saved with success!"))
.catch(error => {
/*
* Here I don't know if:
* 1. The object is repeated
* 2. There was an error while saving (eg. validation failed)
* 3. Server had a hiccup (500)
*/
sender.replyBadRequest(error);
});
});
This is a problem, because the client will always get the same error message, no matter what and I need error differentiation!
Research
I found a possible solution, based on the division of logic and error/response handling:
Handling multiple catches in promise chain
However, I don't understand a few things:
I don't see how, at least in my example, I can separate the logic from the response. The response will depend on the logic after all!
I would like to avoid error sub-classing and hierarchy. First because I don't use bluebird, and I can't subclass the error class the answer suggests, and second because I don't want my code with a billion different error classes with brittle hierarchies that will change in the future.
My idea, that I don't really like either
With this structure, if I want error differentiation, the only thing I can do is to detect an error occurred, build an object with that information and then throw it:
.then(doc => {
if(doc === null)
throw {reason: "ObjectNotFound"};
//do stuff
return doc.save();
})
.catch(error => {
if(error.reason === "ObjectNotFound")
sendJsonResponse(res, 404, err);
else if(error.reason === "Something else ")
sendJsonResponse(/*you get the idea*/);
else //if we don't know the reasons, its because the server likely crashed
sendJsonResponse(res, 500, err);
});
I personally don't find this solution particularly attractive because it means I will have a huge if then else chain of statements in my catch block.
Also, as mentioned in the previous post, generic error handlers are usually frowned upon (and for a good reason imo).
Questions
How can I improve this code?
Objectives
When I started this thread, I had two objectives in mind:
Having error differentiation
Avoid an if then else of doom in a generic catcher
I have now come up with two radically distinct solutions, which I now post here, for future reference.
Solution 1: Generic error handler with Errors Object
This solution is based on the solution from #Marc Rohloff, however, instead of having an array of functions and looping through each one, I have an object with all the errors.
This approach is better because it is faster, and removes the need for the if validation, meaning you actually do less logic:
const errorHandlers = {
ObjectRepeated: function(error){
return { code: 400, error };
},
SomethingElse: function(error){
return { code: 499, error };
}
};
Survey.findOne({
_id: "bananasId"
})
.then(doc => {
//we dont want to add this object if we already have it
if (doc !== null)
throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists."};
//saving empty object for demonstration purposes
return new Survey({}).save();
})
.then(() => console.log("Object saved with success!"))
.catch(error => {
respondToError(error);
});
const respondToError = error => {
const errorObj = errorHandlers[error.reason](error);
if (errorObj !== undefined)
console.log(`Failed with ${errorObj.code} and reason ${error.reason}: ${JSON.stringify(errorObj)}`);
else
//send default error Obj, server 500
console.log(`Generic fail message ${JSON.stringify(error)}`);
};
This solution achieves:
Partial error differentiation (I will explain why)
Avoids an if then else of doom.
This solution only has partial error differentiation. The reason for this is because you can only differentiate errors that you specifically build, via the throw {reaon: "reasonHere", error: "errorHere"} mechanism.
In this example, you would be able to know if the document already exists, but if there is an error saving the said document (lets say, a validation one) then it would be treated as "Generic" error and thrown as a 500.
To achieve full error differentiation with this, you would have to use the nested Promise anti pattern like the following:
.then(doc => {
//we dont want to add this object if we already have it
if (doc !== null)
throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists." };
//saving empty object for demonstration purposes
return new Survey({}).save()
.then(() => {console.log("great success!");})
.catch(error => {throw {reason: "SomethingElse", error}});
})
It would work... But I see it as a best practice to avoid anti-patterns.
Solution 2: Using ECMA6 Generators via co.
This solution uses Generators via the library co. Meant to replace Promises in near future with a syntax similar to async/await this new feature allows you to write asynchronous code that reads like synchronous (well, almost).
To use it, you first need to install co, or something similar like ogen. I pretty much prefer co, so that is what i will be using here instead.
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
The generator function requestHandler will yield all Promises to the library, which will resolve them and either return or throw accordingly.
Using this strategy, you effectively code like you were coding synchronous code (except for the use of yield).
I personally prefer this strategy because:
Your code is easy to read and it looks synchronous (while still have the advantages of asynchronous code).
You do not have to build and throw error objects every where, you can simply send the message immediately.
And, you can BREAK the code flow via return. This is not possible in a promise chain, as in those you have to force a throw (many times a meaningless one) and catch it to stop executing.
The generator function will only be executed once passed into the library co, which then returns a Promise, stating if the execution was successful or not.
This solution achieves:
error differentiation
avoids if then else hell and generalized catchers (although you will use try/catch in your code, and you still have access to a generalized catcher if you need one).
Using generators is, in my opinion, more flexible and makes for easier to read code. Not all cases are cases for generator usage (like mpj suggests in the video) but in this specific case, I believe it to be the best option.
Conclusion
Solution 1: good classical approach to the problem, but has issues inherent to promise chaining. You can overcome some of them by nesting promises, but that is an anti pattern and defeats their purpose.
Solution 2: more versatile, but requires a library and knowledge on how generators work. Furthermore, different libraries will have different behaviors, so you should be aware of that.
I think a good improvement would be creating an error utility method that takes the error message as a parameter, then does all your ifs to try to parse the error (logic that does have to happen somewhere) and returns a formatted error.
function errorFormatter(errMsg) {
var formattedErr = {
responseCode: 500,
msg: 'Internal Server Error'
};
switch (true) {
case errMsg.includes('ObjectNotFound'):
formattedErr.responseCode = 404;
formattedErr.msg = 'Resource not found';
break;
}
return formattedErr;
}
I have a native javascript promise chain that looks a little like this:
function chain() {
promiseFunction1().then(function(data) {
console.log("some message");
return promiseFunction2();
}).then(function(data) {
console.log("some message");
return promiseFunction3();
}).then(function(data) {
console.log("some message");
return promiseFunction4();
}).catch(function(error) {
console.error(error.stack);
});
}
the promise functions would look a little like this:
function promiseFunction() {
return new Promise(function(resolve,reject) {
someCallbackfunction(arg, function(err, data){
if (err) {
return reject(err);
}
console.log("some message");
return resolve(data);
});
});
}
My code seems to resolve fine from what I can tell (no errors and I can tell from terminal feedback that the operations I needed started running) but no matter what I seem to try I cannot for the life of me seem to get any form of console logging.
1) Why are these statements not printing like I expect them to?
2) How can I go about getting back my verbose output?
It turned out that in the end the problem was external.
Somewhere in my promise chain I called an external library with a callback function but the library never responded causing my chain to wait forever.
If you experience something similar I encourage you to double check all of the functions in the chain for a similar occurrence.
A quick way to debug this could be to place a timeout in each promise function in the chain that resolves it after x amount of time, that way you can at least get your logging results without having to stumble in the dark forever.