Node.js async/await try catch flow - best practice - javascript

I moved to async/await from promises lately. However, I'm uncertain about the try...catch right flow. I have the following example:
const rp = require('request-promise');
const func2 = async () => {
const response1 = await rp('http://nodejs.org/dist/index.json');
if (response1) {
return await rp('http://headers.jsontest.com'); // #opt 1: return a value
// return rp('http://headers.jsontest.com'); // #opt 2: return a promise:
}
}
(func1 = async () => {
try {
const response = await func2();
console.log('Success!');
} catch(e) {
console.log('Failed!');
}
})();
I was wondering if I should add try...catch to func2, but I came to conclusion it's already being handled in the try...catch of func1.
Is this code valid?

Is this code valid?
Yes. (In terms of the try/catch aspect, see "side note" below.)
I was wondering if I should add try...catch to func2, but I came to conclusion it's already being handled in the try...catch of func1.
Yes, it is. Just like with non-async functions, you only need the try/catch in func2 if you want to handle the error there (entirely, or partially and then (re)throwing). If you don't, allow it to propagate to the caller. Just ensure it's handled somewhere.
Side note: I'm assuming your func2 has a return in the case where response1 is a falsy value. If not, func2's promise's resolution value will be undefined in that case.
Side note 2: I'm guessing you've added the func1 = part there just to give yourself a name to refer to in the question and it's not in your real code. But in case it is: Unless you've declared it somewhere you haven't shown, in your example func1 is an implicit global. Recommend avoiding those. :-) More in my blog post: The Horror of Implicit Globals
Side note 3 (he does go on a bit, doesn't he?): If you're not using func1 anywhere else (just using it as a wrapper for convenient await use within it), if you want to, you can avoid a level of indentation by using the fact that async functions return promises:
(async () => {
const response = await func2();
console.log('Success!');
})().catch(e => {
console.log('Failed!');
});
The up side is that it's a bit more concise. The downside is that it mixes metaphors. :-) That and the try/catch do exactly the same thing, it's purely a matter of how you prefer to write it, which is entirely up to you.

Related

Because I can't run await on the top level, I have to put it into an async function - why can I then call that async function directly?

I have a short Node.js script where I require another package and call an async function from it and subsequently want to print the return value. If I simply await the return value from the top level, then I'll get an error, saying that I can only use await inside an async function itself. So apparently the way to go is like this:
async function main() {
foo = await someOtherAsyncFunc();
console.log(foo);
}
main()
Or:
(async function() {
foo = await someOtherAsyncFunc();
console.log(foo);
})();
Or:
(async () => {
foo = await someOtherAsyncFunc();
console.log(foo);
})();
(Credit to VLAZ in chat https://chat.stackoverflow.com/transcript/message/54186176#54186176)
This works - but I want to understand the reasons behind it a little bit more: I'm used to not being able to directly use await from the top level. However, I'm also used to having to call some special library function to actually "venture" into async from the top level. In Python, see asyncio.run for example. What's the point of requiring await to be inside an async function - if I can then call just any async function from the top level? Why then isn't await available at top level, too?
Top-level await used to not be a thing, but it is possible now in ES6 modules.
One reason why top-level await used to not be a thing, and is still not a thing outside of modules is that it could permit syntactical ambiguity. Async and await are valid variable names. outside of modules. If a non-module script permitted top-level await, then, short of re-working the specification (and breaking backwards compatibility), there would be circumstances when the parser couldn't determine whether a particular instance of await was a variable name, or was used as the syntax to wait for the Promise on its right-hand side to resolve.
To avoid any possibility of ambiguity, the parser, when parsing a section of code, essentially needs to have flags that indicate whether await is valid as an identifier at any given point, or whether it's valid as async syntax, and those two must never intersect.
Module scrips permit top-level await (now) because the use of await as an identifier has always been forbidden in them, so there is no syntactical ambiguity.
In contrast, there are zero issues with using .then on the top level because it doesn't result in any ambiguity in any circumstances.
Why doesn't it just return a Promise which is never executed because it doesn't get awaited?
Promises aren't really "executed". They can be constructed, or waited on to fulfill, or waited on to reject. If you have a Promise, you already have some ongoing code that will (probably) eventually result in a fulfillment value being assigned to the Promise.
Hanging Promises are syntactically permitted - values that resolve to Promises but which aren't interacted with elsewhere. (Which makes sense - every .then or .catch produces a new Promise. If every Promise had to be used by something else, you'd end up with an infinite regress.)
Doing
(async () => {
foo = await someOtherAsyncFunc();
console.log(foo);
})();
is essentially syntax sugar for
someOtherAsyncFunc()
.then((foo) => {
console.log(foo);
});
There's no need to tack anything else onto the end of either of those. (though it's recommended to add a .catch to a dangling Promise so unhandled rejections don't occur)

What guarantees exist in asynchronous JavaScript?

I am new to asynchronous code in general (and JavaScript in particular) and have been falling prey to common pitfalls, so I want to re-evaluate my most basic assumptions.
Testing the code below does not give me an answer since, just because the test passed, doesn't mean the code will run the same way in the future.
The question is very general, so I will give a couple concrete examples of the basics I am trying to understand.
First Example
const secret_key = "BLAH BLAH";
var url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
If the above code occurs in an async function, can I be sure that url will be defined correctly (i.e. that the second statement will occur after the first)? Does this change if secret_key is a var? Or let?
Second Example
blah = async function() {
syncFunction();
}
Can I be certain that the synchronous function will be run to completion before the function returns (even if it has to do some time-intensive work)?
I ask because my understanding is that this would not be true for an asynchronous function (as explained here) unless I use await / promises.
Update
In the end, this Stack Overflow question resolved my problem. Basically I was using await, but the function being called had an undefined Promise.
First example
can I be sure that url will be defined correctly (i.e. that the second
statement will occur after the first)?
Why yes, absolutely. These are just two regular lines of codes, you simply store two strings in two variables; of course they will be executed in order, there's nothing asynchronous here.
Second example
Using Typescript instead of Javascript helped me a lot to understand how async functions work. Here's your example in Typescript :
const syncFunction = ():string => {
return "hello"
}
const blah = async ():Promise<string> => {
return syncFunction()
}
(async () => {
const sayHello:string = await blah();
console.log(sayHello) // "hello"
})()
As you can see, adding the types helps.
An async function always returns a Promise. If you return true, then the returned type isn't boolean, it is Promise<boolean>.
So, return syncFunction() returns a Promise of "hello". Therefore, you must await it to actually get your "hello" string. Then you can log it.
Same code transpiled to JS so it is runnable in a snippet :
const syncFunction = () => {
return "hello"
}
const blah = async () => {
return syncFunction()
}
(async () => {
const sayHello = await blah();
console.log(sayHello) // "hello"
})()

Why is async required to call await inside a JavaScript function body? [duplicate]

This question already has answers here:
Why do I have to put async keyword to functions which have await keywords?
(1 answer)
JS async/await - why does await need async?
(3 answers)
Why 'await' requires 'async' in function definition
(4 answers)
Nodejs why is await only restricted to async functions?
(3 answers)
ES2017 - Async vs. Yield
(4 answers)
Closed 2 years ago.
One has to use async keyword on the containing function to use the await inside the function body.
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
fetchMovies();
The await is being used to block on completion of the asynchronous fetch() call. As can be seen in the code, the function fetchMovies() is not even returning any value. And even if it did, it would affect the way the return value is consumed by the caller, but why should it matter to the call to another asynchronous call from the function body?
My question is why is this required? Is there some good explaination of that? Is it related to the need for actual implementation of await and supporting it in older JavaScript versions?
I know iffi pattern is used be able to use await but does that change the semantics for the code that follows the iffi code block in any way?
(async () => {
const response = await fetch('/movies');
console.log(response);
})();
I am also aware of top level await being supported in modules.
May be I am missing something really obvious.
There are three reasons the async keyword exists:
In ECMAScript language versions prior to 2015, await was not a keyword. Marking a function async provides a syntactic "bailout" to indicate a breaking change in the language grammar within the body of the function.
This is the most important reason. Without the async keyword, all programs written in ECMAScript 5 or older would no longer work if they used the await keyword as a variable (in fact this was done intentionally in some cases as a polyfill before async/await was standardized), since that would cause a breaking change without the addition of async to the specification. Because of this, async is syntactically necessary to avoid breaking changes to the language.
It provides a convenient marker for parsers, avoiding an infinite look-ahead in order to determine whether or not a function is asynchronous.
This makes parsing more efficient, which is appealing for both ECMAScript implementers and developers, though this reason alone does not make async strictly necessary to the syntax.
async also performs its own transformation on the function, which is done regardless of whether or not the await keyword is present in the body.
Consider the following two functions:
function foo() {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
}
async function bar() {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
}
async performs the following transformation of function bar():
function bar() {
return new Promise((resolve, reject) => {
try {
resolve((/*async function bar*/() => {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
})());
} catch (reason) {
reject(reason);
}
});
}
Those familiar with promises will recognize that we can simplify the above since the Promise constructor executor function will implicitly reject if it throws an error synchronously:
function bar() {
return new Promise((resolve) => {
if (Math.random() < 0.5) {
return resolve('return');
} else {
throw 'throw';
}
});
}
There are two questions being asked here. Why do you need the async keyword to indicate an async context, and why do you need async context to use await?
If we didn't use the async keyword to indicate an async context, then we would have backwards compatibility issues. Before the async/await update, "await" was a valid variable name. So to make it a reserved word, we need to introduce a new context, the async function context.
The second question is, why do we want this in the first place? Why do we want to differentiate async functions from traditional synchronous code? As the name implies, async functions do not execute all their code at once. When a function "awaits", it effectively stops execution, and the remainder of the code becomes an event handler for the thing being awaited. This allows other code to run.
Browser javascript implementations are single-threaded. Only one thing can be performed at a time, but the event queue allows functions to take turns. Consider what would happen if you could await from a synchronous function. The synchronous code, by definition, does not give up control. Therefore, the async function it's waiting for will never get a chance to swap in to your single execution thread, and you will be waiting forever. So, you can only await an async function if you are already in an async context.
I assume your exact question is this: "Handling return values (null or something) depends on the consumer who called the function. They "should" supposedly get it even if another asynchronous function is called in-between. So why does it matter to wait before further other asynchronous calls?"
You see, such fetch() calls are done in Databases within the duration of "initiating" and "closing" the connection. Almost every method used is asynchronous in this case. So while you're executing fetchMovies(); The thread might move further and execute connection.close(); before the fetching is resolved and returned.
The exact scenarios are like this:
await client.connect(); //connection establishment
// Your purposeful methods
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
await fetchMovies();
// Closing connection to avoid attacks and maintain concurrency
await client.close();
If any method, in this case, is called in an asynchronous manner, the whole session of a Database connection is wasted and our function would return undefined or throw an error "Cannot use a session that has ended"
So we need to "wait" for the "Pending" Promises to reach a "Fulfilled" or "Rejected" state before executing further calls.
You can refer more to this article: Using Promises, async / await with MongoDB just for the sake of understanding.
I think it's to make it clear that the function contains asynchronous code. Let's use another example that does return something:
async function canUseGeolocation() {
const result = await navigator.permissions.query({name: 'geolocation'});
return result.state;
}
The async keyword tells the javascript engine that the function should return a promise, and any return statements in the function should resolve that promise. What happens if we modify it to cache the values so we don't always call the await?
function canUseGeolocation() {
if (cachedPermissionState) return cachedPermissionState;
const result = await navigator.permissions.query({name: 'geolocation'});
cachedPermissionState = result.state;
return result.state;
}
How should javascript know that the function should return a promise? Because it contains an await? What if you later change the function so that the cachedPermissionState is set elsewhere and this function only returns that value so you remove the await? Should that first return statement return a promise or return the value? That would now change the whole way the function is executed and what is returned by return cachedPermissionState;. By using async, we can know that it really returns a promise that the return statement resolves without scanning the function for await statements to determine if it should be treated as async or not.

Is there a practical advantage to returning a pending promise? [duplicate]

This question already has answers here:
Difference between `return await promise` and `return promise`
(7 answers)
Closed 4 years ago.
For the most part, I think I understand how async functions work in JavaScript / Node.js, and I am familiar with the syntax of async/await. But this question is about a very specific case, which I can’t seem to find anywhere, and I think it involves the inner workings of await, which is beyond my understanding.
First I’ll start off with a simple example, and then I’ll extend it to real working code. (Note: I’m actually using Typescript, so you’ll see void instead of undefined, but that’s not important to the problem.)
async function subtask() {
// doSomeStuff();
return; // returns a `Promise<void>`
}
// async function run() {
// see options below
// }
function app() {
run().catch((e) => console.error(e));
}
Option 1
async function run() {
await subtask(); // resolves to `void`
return; // returns a new `Promise<void>`
}
Option 2
async function run() {
return subtask(); // returns a new Promise, resolving to the `Promise<void>` of `subtask()`
}
In the simple example above, I have an async function run that calls a smaller async function subtask. Both functions must return a Promise<void>. I have two options: (1) to await the smaller function and return a new Promise<void>, or (2) to return a wrapped promise, given by the smaller function, which will later resolve to void.
My lack of understanding is about how this works. In Option 1, is execution is paused before subtask() returns? What does that actually mean? Does that mean that the async subtask executes synchronously? Does that mean that app, which calls run(), will also pause execution? What if app was async, would that make a difference?
Is it “better” (more performant) to let the promise bubble up and resolve later, or to resolve it directly within the run function?
The reason this is important is that in my real code I have a bunch of smaller subtasks, which all return void, and then the big function must also return void—it cannot return an array. (Note that the subtasks need not run in any particular order.)
Option 1
async function run() {
await Promise.all([
subtask0(),
subtask1(),
subtask2(),
]);
return;
}
Option 2
async function run() {
return Promise.all([
subtask0(),
subtask1(),
subtask2(),
]).then((_all) => {});
}
function app() {
// do some stuff
run(); // if `run` contains `await`, does execution pause here?
// what if `app` was async?
// do some more stuff
}
It is always better to let the Promise bubble up. This avoids the creation of an extra Promise object that will also be awaited on (whether this is optimized away behind the scenes in whichever JavaScript engine you're using is up to debate, though).
async function run() {
await subtask();
return;
}
This creates an extra Promise (and subsequently an extra callback in the chain that will be executed).
async function run() {
return subtask();
}
This isn't actually doing what you think it is. This is also creating an extra Promise (since you're using the async keyword) and is almost functionally the same as the previous example. By using the async keyword, you're creating and returning a new Promise that will resolve/reject to the same value as the Promise created from the call to subtask(). If you remove the async keyword, then this will avoid the creation of the extra unnecessary Promise .
Now with your Promise.all() examples, I think they're both optimal (assuming you remove the unnecessary async keyword from the 2nd one, like noted above). The first would create 2 Promise s (the Promise.all() one, and the one created from returning from an async function), and the 2nd would as well (the Promise.all() one, and the one created from calling then()). Whichever you'd want to use is basically up to personal choice. Personally, I like the 2nd example since it's not mixing the use of async functions and Promises, which I think makes it a little easier to understand.
And for the last part of your question, execution will be paused wherever the await keyword exists (so within the run() call).
You can also essentially think of the await keyword transforming your code from this:
// Do some stuff
let result = await run();
// Do some other stuff
to this:
// Do some stuff
run().then(result => {
// Do some other stuff
};
(the whole point of await is to reduce confusion/complexity caused by nesting)

ES2017 - Async vs. Yield

I am confused about the current discussion of adding async functions and the keyword await to the next EcmaScript.
I do not understand why it is necessary to have the async keyword before the function keyword.
From my point of view the await keyword to wait for a result of a generator or promise done, a function's return should be enough.
await should simple be usable within normal functions and generator functions with no additional async marker.
And if I need to create a function what should be usable as an result for an await, I simply use a promise.
My reason for asking is this good explanation, where the following example comes from:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
It also could be done as normal function, if the execution of a function will wait for finishing the hole function until all awaits are fulfilled.
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
In my opinion the whole function execution is holding until the next tick (await fulfillment) is done. The difference to Generator-Function is that the next() is triggering and changing the object's value and done field. A function instead will simple give back the result when it is done and the trigger is a function internal trigger like a while-loop.
I do not understand why it is necessary to have the async keyword before the function keyword.
For the same reason that we have the * symbol before generator functions: They mark the function as extraordinary. They are quite similar in that regard - they add a visual marker that the body of this function does not run to completion by itself, but can be interleaved arbitrarily with other code.
The * denotes a generator function, which will always return a generator that can be advanced (and stopped) from outside by consuming it similar to an iterator.
The async denotes an asynchronous function, which will always return a promise that depends on other promises and whose execution is concurrent to other asynchronous operations (and might be cancelled from outside).
It's true that the keyword is not strictly necessary and the kind of the function could be determined by whether the respective keywords (yield(*)/await) appear in its body, but that would lead to less maintainable code:
less comprehensible, because you need to scan the whole body to determine the kind
more errorprone, because it's easy to break a function by adding/removing those keywords without getting a syntax error
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
That sounds like you want a blocking function, which is a very bad idea in a concurrent setting.
By marking a function as async, you're telling JS to always return a Promise.
Because it will always return a Promise, it can also await on promises inside of its own block. Imagine it like one giant Promise chain - what happens internally to the function gets effectively gets bolted on to its internal .then() block, and what's returned is the final .then() in the chain.
For example, this function...
async function test() {
return 'hello world';
}
... returns a Promise. So you can execute it like one, .then() and all.
test().then(message => {
// message = 'hello world'
});
So...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
Is roughly analogous to...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
In both cases, the callback passed to test().then() will receive report as its first parameter.
Generators (i.e. marking a function * and using the yield keyword) are a different concept altogether. They don't use Promises. They effectively allow you to 'jump' between different portions of your code, yielding a result from inside of a function and then jumping back to that point and resuming for the next yield block.
Although they feel somewhat similar (i.e. 'halting' execution until something happens somewhere else), async/await only gives you that illusion because it messes with the internal ordering of Promise execution. It's not actually waiting - it's just shuffling when the callbacks happen.
Generators, by contrast, are implemented differently so that the generator can maintain state and be iterated over. Again, nothing to do with Promises.
The line is further blurred because at the current time of writing, support for async/await is scare; Chakracore supports it natively, and V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await and convert the code to generators. It's a mistake to conclude that generators and async/await are therefore the same; they're not... it just so happens that you can bastardize how yield works alongside Promises to get a similar result.
Update: November 2017
Node LTS now has native async/await support, so you ought never to need to use generators to simulate Promises.
These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.
The reason is that this was a valid JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)
So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.
The reason for the async keyword in front is simple so you know that return value will be transformed into a promise. If no keyword how would the Interpreter know to do this.
I think this was first introduce in C# and EcmaScript is taking a loot of stuff from TypeScript. TypeScript and C# are conceived by Anders Hejlsberg and are similar.
lets say you have a function (this one is just to have some asynchronous work)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
this function will make us wait for a random time and return a Promise (if you use jQuery Promise is similar to Deferred) object. To use this function today you would write something like this
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
And this is fine. Now lets try to return the log message
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
Ok this is not bad but there are two return statements and a function in the code.
Now with async this will look like this
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
The code is short and inline. If you written a lot of .then() or . done() you know how unreadable the code can get.
Now why the async keyword in front of function. Well this is to indicate that your return value is not what is returned. In theory you could write this(This can be done in c# I don't know if js will allow since it's not done).
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
you see, you return a number but the actual return will be a Promise you don't have to use
return new Promise(function(resolve, reject) { resolve(5);};
Since you can't await a number only a Promise await test(false) will throw an exception and await test(true) will not if you don't indicate async in front.

Categories

Resources