A great pattern with ES2017 async/await is:
async function () {
try {
var result = await some_promised_value()
} catch (err) {
console.log(`This block would be processed in
a reject() callback with promise patterns
but this is far more intuitive`)
return false // or something less obtuse
}
result = do_something_to_result(result)
return result;
}
Being able to handle errors like that is really nice. But say I want to asynchronously get a value I'd like to protect it from reassignment (eg: a database session), but I still want to use the async/await pattern (purely because I think it's far more intuitive).
The following won't work because const is block scoped:
async function () {
try {
const result = await get_session()
} catch (err) {
console.log(`This block should catch any
instantiation errors.`)
return false
}
// Can't get at result here because it is block scoped.
}
Of course you could execute the rest of the function within the try block, but then you risk errors getting caught that should be let fall through somewhere else. (For example I hit this challenge writing tests where errors like test failures need to fall back to the test suite but wanted to handle driver instantiation myself. Perhaps I'm falling into some kind of anti-pattern here by not letting those errors fall back to the test suite.)
The simple answer obviously is don't use that pattern here. Use Promises and callbacks instead. Or use a var and avoid block scoped const and let. But I like the benefits of reassignment protection and block scope for other considerations.
[question]: Is there a way to wrap an await/async try/catch block to every function? Seems to be one potential solution, but doesn't quite come up as readable as try/catch. The fact that try/catch doesn't break function scope and I can use return from within a try/catch block seems to me to be more in line with the spirit of async/await bringing a more procedural-looking logic to asynchronous code.
Ideally you'd want to do something like const x = await y() catch (err) or const x = await y() || fail But I can't think of anything with a similar flow that's syntactically correct.
UPDATE:
As suggested by #jmar777 below another alternative is:
const x = await y().catch(() => {/*handle errors here*/})
Which is probably the closest I've found to those last two examples in practice. But it breaks the return scope that blocks downstream execution in the examples above. Which is a nice synchronous-like way of handling things.
Each solution has its trade offs.
I've manually hoisted the variables with let at the top of the function block as a working solution (as described in jmar777's answer) still an interesting question to see the various approaches, so I'll leave the question open for now.
You may have a misunderstanding regarding the benefits of using const. Assigning a value using a const declaration doesn't make that value immutable, it simply means that the value of that constant can't be changed or redeclared:
The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. For instance, in case the content is an object, this means the object itself can still be altered. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const)
In your example, you're probably best off manually hoisting your result binding outside of the try/catch:
async function () {
let result;
try {
result = await get_session()
} catch (err) {
console.log(`This block should catch any
instantiation errors.`)
}
// do whatever else you need with result here...
}
An alternative would be to restructure the code such that you don't need to rely on a try/catch at all. For example:
async function () {
const result = await get_session().catch(someRejectionHandler);
// do whatever else you need with result here...
}
Just be aware that your downstream code needs to gracefully handle the case where get_session() was rejected, and result isn't initialized based on a successful response. This is no different than in your initial example, but perhaps not quite as obvious when scanning the code.
Coming back to this question a few years later a far simpler answer has occurred to me, I don’t see in retrospect why I was interested in a small try/catch pair and then performing more logic outside of it when I could simply perform all the logic and return inside the try statement.
Taking the example from the question and applying that structure it would be:
async function () {
try {
const result = await get_session()
// Do what needs to be done and then:
return result
} catch (err) {
console.log(`This block should catch any
instantiation errors.`)
return false
}
}
If you need to access anything from the try block in the catch block raise it in an error. There may be a reason I needed to move out of the try scope when I asked this question, but I can’t see a scenario that would necessitate it looking back. This solution wouldn’t work if you were to use try/catch/finally and return in finally. But I don’t think I’ve come across that pattern much at all in the wild.
Related
I've been using #zzzzBov's fantastic loadScript() function to load JS libraries using Promises.
However, it becomes tedious and messy to chain together Promises for all of the loaded libraries. I'd like to switch to an async/await solution, but—since loadScript() function doesn't return the usable library objects I need—this isn't as straightforward as using .then() at a first glance.
The function returns a load Event, which I await. So my initial approach would be to just define some useless constants as the Promise returns, and then reference them in an if statement:
let loadedGlobalObject: any;
const lib1Loaded: Event = await loadScript(lib1URL);
const lib2Loaded: Event = await loadScript(lib2URL);
if (lib1Loaded && lib2Loaded) { // Makes this block dependent upon the await statements
loadedGlobalObject = (window as any).globalObject;
console.log(loadedGlobalObject);
}
Is this correct form? I don't know how to "invoke" the await statements; an if statement was my first guess. So I'm wondering if this is the best solution, or if there is a more efficient/accepted technique (I know that I could substitute the libXLoaded constants into the if statement, but I'm going for readability).
If I understand what you are trying to do - you want to await the loading of your libraries, and access the global objects only once they are loaded. What about something like this:
try {
async function loadLibs () {
const lib1Event: Event = await loadScript(lib1Url);
let loadedGlobalObject: any;
loadedGlobalObject = (window as any).globalObject;
console.log(loadedGlobalObject);
}
loadLibs();
catch (err) {
console.log('Error loading script: ', err);
}
The loadScript() function rejects if there was an error while loading the script, so you can wrap your code in a try...catch. Otherwise, it resolves and you can access it's global object.
Note that you can only call await within a function marked as async.
So it turns out I didn't understand async/await at all. I had thought that the awaited constants were reactive, and lines referencing them were run only after their Promises resolved; instead, it's just the rest of the function that waits.
For error-handling, as #dwosk answered, you can use a try-catch, although I wasn't concerned about this. As #Rezaa91 commented, I could have used an await Promise.all([]), except for that I'm loading dependencies in order. And, now that I understand await statements better and as #dwosk and #Bergi noted, I don't need to define those constants.
In summary, my modified code (with Promise.all() added for fun) looks like this:
let loadedGlobalObject: any;
await Promise.all([loadScript(dependency1), loadScript(dependency2)]);
await loadScript(library1);
// Will have been loaded by now.
loadedGlobalObject = (window as any).globalObject;
console.log(loadedGlobalObject);
I need to know if it is better to use asynchronous createConnection or not
Does this change anything at loading speed?
I'm using express, ReactJS, promise-mysql
What should I use?
This:
async connect () {
try{
const conn = await db.createConnection(this.config);
this.conn = conn;
} catch(error){
console.log(error)
}
}
Or this
connect () {
return db.createConnection(this.config).then(conn => {
this.conn = conn
})
}
Well, the error handling is completely different in your two examples. In the first, you log the error and allow the returned promise to then be resolved. In the second, a connection error will reject the returned promise. So, that's a major structural difference.
If you changed the first one to this:
async connect () {
this.conn = await db.createConnection(this.config);
}
Then, that would be structurally the same as your second example:
connect () {
return db.createConnection(this.config).then(conn => {
this.conn = conn;
});
}
Now, if you compare these two they have the same outcomes (except in an edge case where db.createConnection() would throw synchronously which it hopefully doesn't do).
So, then if you reasked your question based on these two that have the same outcomes, the answer is that it doesn't really make a difference.
If there was a measurable difference in execution speed, it would be so small as to be very unlikely to be meaningful and whatever difference there was would be only dependent upon the particular JS engine implementation and likely not constant as the JS engine matures.
So, it's really just a matter of coding style and which you prefer. The await version is less typing, less lines of code (as it often is). I personally tend to not use async/await unless I have more than one asynchronous operation that I'm trying to sequence, but that's perhaps just some inertia from coding with .then() for awhile before await came along.
Is it good practice to wrap a try/catch with a Promise? I was reviewing the code below, I cannot seem to understand the necessity of having a try/catch block inside a Promise. Also, why would there be no place where the Promise rejects, as you can see in the catch block, the Promise resolves. Please help me understand this piece of code, in terms of best practice and efficiency the point of having the try/catch inside the Promise.
I would also really appreciate any suggestions regarding cleaning the code up where it is redundant.
getRoute(): any {
return async (request: any, reply: any) => {
const result = new Promise<string>( async (resolve, reject) => {
try {
let userIsValid = await this.validator.validate(request.payload, RegisterUserValidator);
if (userIsValid.error) {
resolve(responseHelper.getErrorResponse(ResponseErrorCode.joiValidatorError, userIsValid.error));
throw new ControlFlowNonError();
}
let results = await this.authUser.registerUser(request.payload);
resolve(responseHelper.getSuccessResponse(results, null));
}
catch (error) {
let message: ILogMessage = {
code: ResponseErrorCode.unknownError,
message: ResponseErrorCode.unknownError.toString(),
meta: error,
sourceFunction : 'ApiDataCreateCredentials: getRoute()'
};
this.logHelper.error(message);
resolve(error);
}
});
reply(result);
};
};
Is it best practice to have a try/catch block inside a promise [executor function]?
No, it's not best practice. There's pretty much no reason to use promises inside a promise executor function. Because when you're doing that, you don't need the outer, manually created promise at all. You can just return the inner promise. That's the Promise constructor anti-pattern.
FYI, though it isn't your case here, it is reasonable to use try/catch to handle regular exceptions inside a promise executor (if the manually created promise executor is actually needed in the first place and if regular exceptions are of concern and you want to handle them locally).
Here's another idea for how to accomplish your same logic. This removes the promise anti-pattern of surrounding a promise with another manually created one and uses promise flow control to make userIsValid.error go to the log error code. I didn't see any particular advantage of using await here so I switched back to just using .then() and .catch(). I don't know TypeScript so this is a regular Javascript version of the code, but you can presumably add the slight syntax differences yourself to turn it back to TypeScript:
getRoute(): function() {
return function(request, reply) {
return this.validator.validate(request.payload, RegisterUserValidator).then(userIsValid => {
if (userIsValid.error) {
// treat this as an error condition
throw responseHelper.getErrorResponse(ResponseErrorCode.joiValidatorError, userIsValid.error);
}
return this.authUser.registerUser(request.payload).then(results => responseHelper.getSuccessResponse(results, null));
}).catch(error => {
let message: ILogMessage = {
code: ResponseErrorCode.unknownError,
message: ResponseErrorCode.unknownError.toString(),
meta: error,
sourceFunction : 'ApiDataCreateCredentials: getRoute()'
};
this.logHelper.error(message);
// turn back into resolved promise with error as the result
return error;
}).then(reply); // always call reply
}
}
Things that did not seem ideal with your implementation:
Promise anti-pattern (creating an unnecessary wrapper promise).
Calling resolve() multiple times (the second one will be ignored, but it seems less than ideal to code it that way)
There does not seem to be any particular benefit to using async/await here. It seems to complicate the flow (in my opinion).
resolve() followed by throw is a real head scratcher when reading the code. Yes, one can eventually figure out what you're trying to do, but this is an odd way to do it. My scheme is a bit more semantic. if (userIsValid.error) is really just an error condition for you so coding it this way just makes that more obvious. Then, since you want ALL errors to proceed as if there was no error (after logging), we just make the .catch() handler return normally which allows the last .then() handler to always get called.
Other references related to your topic:
Can't throw error from within an async promise executor function
Is it an anti-pattern to use async/await inside of a new Promise() constructor?
In this function that handles a REST API call, any of the called functions to handle parts of the request might throw an error to signal that an error code should be sent as response. However, the function itself might also discover an error, at which point it should jump into the exception handling block.
static async handleRequest(req) {
try {
let isAllowed = await checkIfIsAllowed(req);
if (!isAllowed) {
throw new ForbiddenException("You're not allowed to do that.");
}
let result = await doSomething(req); // can also raise exceptions
sendResult(result);
} catch(err) {
sendErrorCode(err);
}
}
Webstorm will underline the throw with the following message: 'throw' of exception caught locally. This inspection reports any instances of JavaScript throw statements whose exceptions are always caught by containing try statements. Using throw statements as a "goto" to change the local flow of control is likely to be confusing.
However, I'm not sure how to refactor the code to improve the situation.
I could copypaste the code from the catch block into the if check, but I believe this would make my code less readable and harder to maintain.
I could write a new function that does the isAllowed check and throws an exception if it doesn't succeed, but that seems to be sidestepping the issue, rather than fixing a design problem that Webstorm is supposedly reporting.
Are we using exceptions in a bad way, and that's why we're encountering this problem, or is the Webstorm error simply misguiding and should be disabled?
Contrary to James Thorpe's opinion, I slightly prefer the pattern of throwing. I don't see any compelling reason to treat local errors in the try block any differently from errors that bubble up from deeper in the call stack... just throw them. In my opinion, this is a better application of consistency.
Because this pattern is more consistent, it naturally lends itself better to refactoring when you want to extract logic in the try block to another function that is perhaps in another module/file.
// main.js
try {
if (!data) throw Error('missing data')
} catch (error) {
handleError(error)
}
// Refactor...
// validate.js
function checkData(data) {
if (!data) throw Error('missing data')
}
// main.js
try {
checkData(data)
} catch (error) {
handleError(error)
}
If instead of throwing in the try block you handle the error, then the logic has to change if you refactor it outside of the try block.
In addition, handling the error has the drawback of making you remember to return early so that the try block doesn't continue to execute logic after the error is encountered. This can be quite easy to forget.
try {
if (!data) {
handleError(error)
return // if you forget this, you might execute code you didn't mean to. this isn't a problem with throw.
}
// more logic down here
} catch (error) {
handleError(error)
}
If you're concerned about which method is more performant, you shouldn't be. Handling the error is technically more performant, but the difference between the two is absolutely trivial.
Consider the possibility that WebStorm is a bit too opinionated here. ESLint doesn't even have a rule for this. Either pattern is completely valid.
You're checking for something and throwing an exception if isAllowed fails, but you know what to do in that situation - call sendErrorCode. You should throw exceptions to external callers if you don't know how to handle the situation - ie in exceptional circumstances.
In this case you already have a defined process of what to do if this happens - just use it directly without the indirect throw/catch:
static async handleRequest(req) {
try {
let isAllowed = await checkIfIsAllowed(req);
if (!isAllowed) {
sendErrorCode("You're not allowed to do that.");
return;
}
let result = await doSomething(req); // can also raise exceptions
sendResult(result);
} catch(err) {
sendErrorCode(err);
}
}
I could copypaste the code from the catch block into the ifcheck, but I believe this would make my code less readable and harder to maintain.
On the contrary, as above, I would expect this to be the way to handle this situation.
Since this is not a blocking error, but only an IDE recommendation, then the question should be viewed from two sides.
The first side is performance. If this is a bottleneck and it is potentially possible to use it with compilation or when transferring to new (not yet released) versions of nodejs, the presence of repetitions is not always a bad solution. It seems that the IDE hints precisely in this case and that such a design can lead to poor optimization in some cases.
The second side is the code design. If it will make the code more readable and simplify the work for other developers - keep it. From this point of view, solutions have already been proposed above.
Return a promise reject instead of throwing error in the try block
try {
const isAllowed = await checkIfIsAllowed(request);
if (!isAllowed) {
return Promise.reject(Error("You're not allowed to do that."));
}
const result = await doSomething(request);
sendResult(result);
} catch (error) {
throw error;
}
There are good answers to the question "Why not use exceptions as normal flow control?" here.
The reason not to throw an exception that you will catch locally is that you locally know how to handle that situation, so it is, by definition, not exceptional.
#James Thorpe's answer looks good to me, but #matchish feels it violates DRY. I say that in general, it does not. DRY, which stands for Don't Repeat Yourself, is defined by the people who coined the phrase as "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system". As applied to writing software code, it is about not repeating complex code.
Practically any code that is said to violate DRY is said to be "fixed" by extracting the repeated code into a function and then calling that function from the places it was previously repeated. Having multiple parts of your code call sendErrorCode is the solution to fixing a DRY problem. All of the knowledge of what to do with the error is in one definitive place, namely the sendErrorCode function.
I would modify #James Thorpe's answer slightly, but it is more of a quibble than a real criticism, which is that sendErrorCode should be receiving exception objects or strings but not both:
static async handleRequest(req) {
try {
let isAllowed = await checkIfIsAllowed(req);
if (!isAllowed) {
sendErrorCode(new ForbiddenException("You're not allowed to do that."));
return;
}
let result = await doSomething(req); // can also raise exceptions
sendResult(result);
} catch(err) {
sendErrorCode(err);
}
}
The larger question is what is the likelihood of the error and is it appropriate to treat !isAllowed as an exception. Exceptions are meant to handle unusual or unpredictable situations. I would expect !isAllowed to be a normal occurrence that should be handled with logic specific to that situation, unlike, say, a sudden inability to query the database that has the answer to the isAllowed question.
#matchish's proposed solution changes the contract of doSomethingOnAllowedRequest from something that will never throw an exception to something that will routinely throw an exception, placing the burden of exception handling on all of its callers. This is likely to cause a violation of DRY by causing multiple callers to have repetitions of the same error handling code, so in the abstract I do not like it. In practice, it would depend on the overall situation, such as how many callers are there and do they, in fact, share the same response to errors.
Answer of James Thorpe has one disadvantage on my opinion. It's not DRY, in both cases when you call sendError you handle Exceptions. Let's imagine we have many lines of code with logic like this where Exception can be thrown. I think it can be better.
This is my solution
async function doSomethingOnAllowedRequest(req) {
let isAllowed = await checkIfIsAllowed(req);
if (!isAllowed) {
throw new ForbiddenException("You're not allowed to do that.");
}
doSomething(req);
}
static async handleRequest(req) {
try {
let result = await doSomethingOnAllowedRequest(req);
sendResult(result);
} catch(err) {
sendErrorCode(err);
}
}
This could give you some tips, maybe that can be the cause(not sure if relevant).
Catch statement does not catch thrown error
"
The reason why your try catch block is failing is because an ajax request is asynchronous. The try catch block will execute before the Ajax call and send the request itself, but the error is thrown when the result is returned, AT A LATER POINT IN TIME.
When the try catch block is executed, there is no error. When the error is thrown, there is no try catch. If you need try catch for ajax requests, always put ajax try catch blocks inside the success callback, NEVER outside of it."
Trying to write a little chrome extension, which relies on the callback-heavy query functions of the chrome.* interface, I quickly landed at promises and async/await, as I needed to guarantee the order of certain operations, while trying to avoid callback hell.
However, once I introduced async/await into some functions, every function that used them also had to be turned into an async function in order to be able to await the return value. Eventually even some global constants became promises, e.g.
const DEBUG = new Promise(function(resolve){
chrome.management.getSelf(resolve);
}).then(function(self){
return self.installType == 'development';
});
However, now I need to write await everywhere and introducing weird bugs like if(DEBUG){...} always being executed becomes way too easy.
While it seems possible to identify the errors using ESLINT, writing await everywhere seems unnecessarily cumbersome and thus I was wondering if Javascript has some better construct that I am missing?
(Subjectively my current use of await/async seems backwards; Promises are kept as-is unless explicitly awaited, but it seems more desirable to me to have promises awaited by default in async functions and kept as bare promises only when explicitly requested.)
For the lack of a type system that would allow to catch such mistakes easily (did you consider Typescript or Flow?), you can use Systems Hungarian Notation for your variable names. Choose a prefix of suffix like P, Promise or $ and add it to all your promise variables, similar to how asynchronous functions are often named with an Async suffix. Then only do things like
const debug = await debugPromise
where you can quickly see that if (debug) is fine but if (debugPromise) is not.
Once I introduced async/await into some functions, every function that used them also had to be turned into an async function in order to be able to await the return value. Eventually even some global constants became promises
I would not do that. Try to make as few functions asynchronous as possible. If they are not doing intrinsically asynchronous things themselves but only rely on the results of some promises, declare those results as parameters of the function. A simple example:
// Bad
async function fetchAndParse(options) {
const response = await fetch(options);
// do something
return result;
}
// usage:
await fetchAndParse(options)
// Good:
function parse(response) {
// do something
return result;
}
// usage:
await fetch(options).then(parse) // or
parse(await fetch(options))
The same pattern can be applied for globals - either make them explicit parameters of every function, or make them parameters of a module function that contains all others as closures. Then await the global promises only once in the module, before declaring or executing anything else, and use the plain result value afterwards.
// Bad:
async function log(line) {
if (await debugPromise)
console.log(line);
}
async function parse(response) {
await log("parsing")
// do something
return result;
}
… await parse(…) …
// Good:
(async function mymodule() {
const debug = await debugPromise;
function log(line) {
if (debug)
console.log(line);
}
function parse(response) {
log("parsing")
// do something
return result;
}
… parse(…) …
}());