Declare existing functions as async? - javascript

I use a function called setConf for testing. Sometimes it's really convenient if I can use it to start a promise chain, sometimes I just need need it to return the value.
Is it possible to declare an existing function as async? What I would like to do but it doesn't work is:
const setConf = (conf) => {
mainConf = Object.assign(mainConf , conf)
return mainConf
}
module.exports.asyncSetConf = async setConf
module.exports.setConf = setConf
this I currently have
exports.asyncSetConf = async (conf) => {
mainConf = Object.assign(mainConf , conf)
return mainConf
}
exports.setConf = (conf) => {
mainConf = Object.assign(mainConf , conf)
return mainConf
}
The code is identical, It seems kinda silly not to be able to refactor this

First off, a function declared with the async keyword returns a promise. So you either want your function to return a promise or you don't. There is no such thing as a function that is declared with the async keyword that sometimes returns a promise and sometimes doesn't.
Second off, if you have no asynchronous code in your function, then it's generally making things more complicated, not less complicated to wrap synchronous operations in an asynchronous interface, so there's generally no reason to do that.
Third off, if you just want to sometimes use setConf() to start a promise chain, you can simply wrap it with Promise.resolve() when you want to do that:
Promise.resolve(setConf(...)).then(f).then(g).catch(...)
Of course, you don't necessarily need to do that either because if f() is asynchronous and returns a promise, you could just do this:
f(setConfg(...)).then(g).catch(...)
If you find yourself using Promise.resolve(setConf(...)) a lot, then you can just make a utility function for it:
function setConfPromise(...args) {
return Promise.resolve(setConf(..args))
}
Is it possible to declare an existing function as async?
You can change its declaration or you can wrap it with another function. You can't dynamically make it sometimes with async behavior and sometimes not. You need two separate functions for that.
You could wrap it like this:
module.exports.asyncSetConf = function(...args) {
return Promise.resolve(setConf(...args));
}
module.exports.setConf = setConf
FYI, async should only be used when actually needed. Some notes about its usage:
You should never use async when there no actual asynchronous code involved. Putting an asynchronous interface on synchronous code just makes use of the function more complicated than required.
Use async when you want to use await inside the function.
Use async when you to force automatically returning a promise and have some exceptions automatically caught and turned into a rejected promise.
Personally, I'd only ever use async when I want to use await inside the function.
You do not need to use async just to return a promise. You can do that the "old-fashioned" way by just returning a promise.
Using async does not make any synchronous code suddenly run asynchronously (a common newbie assumption). It doesn't change synchronous code at all. It forces the return value to be a promise which the caller has to use either await or .then() with to get the value from which will force a "next tick" execution on the .then(), but if the code in the function was all synchronous, it will still execute synchronously - async doesn't change that.

Related

Why doesn't .then() need the async keyword when used (similar to await)? How does Javascript know it's an asynchronous operation?

I'm starting to learn asynchronous Javascript and I'm really confused.
To be honest, the async/await approach seems very logical to me. We need to let the runtime know we're doing an asynchronous operation so it can handle it accordingly. But why don't we need to do the same when using the .then() method? I mean, if Javascript was already able to understand when promises are handled, couldn't await just be used without async just like .then()?
To make it even more confusing, I saw people using .then() directly inside functions declared with the async keyword. Wasn't async/await supposed to be syntactic sugar for the .then().catch() approach? Why can these be combined, especially inside one another? Using .then() on the result of the async function wouldn't have been as confusing, but being inside one another makes me have even a harder time understanding this.
I really searched everywhere for an explanation on this and couldn't find an answer for this exact question. All I've found was people saying you can use both approaches because they essentially are the same thing, but when you get in the details, things aren't very clear.
So the async function always returns a promise. Inside it, await always handles promises. .then() can be chained to the await function. .then() can also be chained to the result of the async function. Same with the .catch method if we don't want to use try/catch on the await. Why is it so mixed up? Can we handle the return of async without .then()? If async/await really is syntactic sugar for .then(), why doesn't .then() also always return a promise after it resolves?
If anybody can help with some clarification, I would truly appreciate it. Thank you!
There's so many questions here, but one way to think about it is... Promises are not asynchronous operations, they are just a standard pattern that helps users deal with existing asynchronous operations in a predicable way.
There's nothing special about .then(). It's not a Javascript keyword, and javascript doesn't need to be 'aware' of then in any way.
Promises are a pattern. You can write a promise class yourself from scratch (I'd highly recommend this). When you use .then(), and you pass it a function, you are telling the promise class: "When the promise resolves, please call this function for me".
All of this existed before async/await. Async/await was added after to make it easier to work with promises.
I find the question if 'something is syntactic sugar' meaningless. It suggests that a language feature is not that meaningful because the same thing could be accomplished before the language feature existed. While this might be true, this is true for any programming language when compared to, say, assembly. To me the definition of 'syntax sugar' can be extended to almost anything, so it's not a useful designation.
What async/await (and generators before it) adds to the language, is that a javascript function can be interrupted and resumed later after some condition.
Lastly, .then() and .catch() always return promises. If you see a .then() function that doesn't, then it's not compatible with the Promise/A+ spec.
If you are willing to put in the work to fully understand this, I would recommend the following 2 exercises:
Write your own Promise class
Re-implement async/await using generator functions (see the co package of how this was done before async/await landed)
The purpose of async/await is to allow writing async code in a serial manner, which is mentally simpler to reason about (for some human-beings). This is useful, if you need to wait for async operation to finish before you continue with the rest of the code. For example, if you need to pass result of async operation as parameter.
Example 1
function asyncOperation1(n) { return Promise.resolve(n+1); }
function asyncOperation2(n) { return Promise.resolve(n/2); }
function asyncOperation3(n) { return Promise.resolve(n*3); }
function errorHandler(err) { console.error(err); }
function main() {
// flow-control
asyncOperation1(1)
.then(asyncOperation2)
.then(asyncOperation3)
.then(continueAfterAsync)
.catch(errorHandler)
// function wrapper
function continueAfterAsync(result) {
console.log(result);
}
}
main();
With async/await the code of the main function above may look like
async main() {
try {
console.log(
await asyncOperation3(
await asyncOperation2(
await asyncOperation1(1)
)
)
);
} catch(err) {
errorHandler(err);
}
}
Pay attention that we don't need to rewrite async operation functions to be async function asyncOperation... to use await, but we need to declare main function as async main.
Which one is better(?) is the mater of developers's taste and previous programming languages experience. The benefit that I can see is that you don't need to wrap everything into functions and introduce additional flow-control code, leaving this complexity to JavaScript compiler.
However, there are cases, when you want to schedule some parallel tasks and you don't care which one will finish first. These kind of things would be relatively hard to do with async/await only.
Example 2
function main() {
Promise
.all(
['srv1', 'srv2', 'srv3'].map(
srv => fetch(`${srv}.test.com/status`)
)
])
.then(
responses => responses.some(res => res.status !== 200) ?
console.error('some servers have problems') :
console.log('everything is fine')
)
.catch(err => console.error('some servers are not reachable', err))
}
So, we see that there is a room for both .then() and await to coexist.
In some cases function may be either synchronous or asynchronous, depending on business logic (I know it's ugly, but in some cases it's unavoidable). And here we come to your main question
why don't we need to mark an asynchronous operation with .then() and we have to do it with await
In other words, why do we need async keyword at all?
Example 3
// without `async`
function checkStatus(srv) {
if (!srv.startsWith('srv')) {
throw new Error('An argument passed to checkStatus should start with "srv"')
}
return fetch(`https://${srv}.test.com/status`);
}
function main() {
// this code will print message
checkStatus('srv1')
.then(res => console.log(`Status is ${res.status === 200 ? 'ok': 'error'}`))
.catch(err => console.error(err));
// this code will fail with
// Uncaught TypeError: (intermediate value).then is not a function
checkStatus('svr1')
.then(res => console.log(`Status is ${res.status === 200 ? 'ok': 'error'}`))
.catch(err => console.error(err));
}
However, if we define async function checkStatus, compiler will wrap the runtime error into rejected promise return value, and both parts of the main function will work.
Now let's imagine that JavaScript allows to write functions that use await without specifying async in front of them.
Example 4 (not a valid Javascript)
function checkStatus(srv) {
if (cache[srv]) {
data = cache[srv];
} else {
data = (await fetch(`https://${srv}.test.com/status`)).json();
}
data.x.y = 'y';
return data;
}
What would you expect checkStatus to return? Promise, raw value or throw exception (in case data.x is undefined)?
If you say Promise, then it would be hard for developer that uses this function to understand why inside of checkStatus one can write data.x and outside of it (await data).x is required.
If raw value, the whole execution flow becomes cumbersome, and you can no longer rely on the fact that JavaScript is a single-threaded language, where no-one can change the value of the variable between two lines of code that are written in serial manner.
As you noticed, async/await is a syntactic sugar. If this syntax allows me to avoid possible runtime errors at earlier stage and keep the language backward compatible, I'm eager to pay the price of putting extra async in front of async functions.
Also, I would recommend to read the answers to JS async/await - why does await need async?
In a simple explanation, async/await is syntactic sugar (the node interpreter/compiler/optimizer will convert everything to normal Promises).
The goal of this feature is turn our life easy, because the callback way/style of programming eventually lead us to make mistakes. We call this "the callback hell"
So, we can use .then() when calling functions that is decorated with async keyword, and we can await on functions that return Promise objects.
It's important for optimizations in general, that the code we write tells the compiler what we are meaning, in terms of performance. Imagine if all our codes / lines of code / instructions could be async or sync at the same time. This would lead the computers perform bad, because the task of check this during runtime is very expensive.
So that's why it's so important to us code instructions in an efficient manner.

Module-scope variable refuses to be overwritten by an async function. Why?

The module-scope variable "output" refuses to be overwritten by the async function "retrieveTextWrapper", and I cannot figure out why. My objective is to output the text on StackOverFlow's homepage. retrieveTextWrapper successfully scrapes this information, but I can't seem to assign this content to the output variable. What am I doing wrong? How can I print the scraped information from the main() function?
Note: I am using electron version 3.0.4 because bypassing CORS is less of a pain on that version.
const {BrowserWindow, app} = require('electron')
output = "this should be overwritten by the retrieveTextWrapper method"
async function main(){
navigate();
win.openDevTools();
await win.webContents.once('dom-ready',retrieveTextWrapper);
console.log(output);
//prints "this should be overwritten by the retrieveTextWrapper method"
}
function navigate() {
win = new BrowserWindow({width:900,height:900});
win.loadURL(`https://stackoverflow.com/`);
}
function retrieveText(){
return `document.querySelector("*").innerText`;
}
async function retrieveTextWrapper(){
output = await win.webContents.executeJavaScript(retrieveText().replace("*", "#content"));
}
app.on('ready',main)
win.webContents.once() does not return a promise (since interfaces generally don't accept both callbacks and return a promise at the same time).
Therefore await doesn't wait for the asynchronous operation to complete. Therefore, you're looking at output before its value has been reassigned. await only does something useful when you await a promise that is connected to the asynchronous operation you're trying to wait for.
To confirm this timing issue, add a unique console.log() statement before and after the await win.webContents.once('dom-ready',retrieveTextWrapper); and inside of retrieveTextWrapper and then you can see the sequencing of these log messages.
Yep, everything changes as it should within retrieveTextWrapper function. And your explanation makes a lot of sense. However, is it possible to wait for the callback to finish (using some other syntax aside from await)? That way, I can use the updated value for other operations in the main function?
You have a couple options.
You could "promisify" win.webContents.once() so you could then use await with it.
You could put the callback inline and put the rest of your code in main inside that callback (a classic way of dealing with asynchronous operations).
Here's an example of promisifying win.webContents.once():
function waitForDomReady() {
return new Promise((resolve, reject) => {
// may want to check if document already has dom-ready and resolve immediately
win.webContents.once('dom-ready', resolve);
});
}
And, you could then use it like this:
async function main(){
navigate();
win.openDevTools();
await waitForDomReady();
await retrieveTextWrapper();
console.log(output);
}
This assumes that the code in retrieveTextWrapper that calls win.webContents.executeJavaScript() does actually return a promise when it's done. If not, you have to promisify that too.

Why should we wrap await inside an async function?

Why should we have an async function in order to use await? Why can't we just use await without async? JS is async by default too, this just adds to the confusion.
Update:
I've seen some lads put my question on hold so I'll try to elaborate.
I'm just curious as to why this won't work:
some code
let users = await getUsers();
some code
Why should it be inside an async for it to work, ie
$(async function() {
some code
let users = await getUsers();
some code
});
JS is async by default too...
No, JavaScript is not async by default. The only async features of JavaScript are fairly newly-added:
Promise resolution
async/await
JavaScript is commonly used in environments where you interact with asynchronous things (like event handlers in the DOM, or I/O completions in Node.js), but JavaScript is not asynchronous (other than above).
In the words of Allen Wirfs-Brock, who was the editor of the ECMAScript specification for many years, JavaScript...
(has) an observably synchronous execution model. Other than via Atomics/SABs there are no observable shared-state race conditions.
Back to your question:
Why should we have an async function in order to use await?
Before too long, with modules you won't have to, once the top level await proposal finishes working through the process. It just got to Stage 3.
But the answer is that await is syntactic sugar for consuming a promise, and one of the rules of promises is that you either handle errors or return the chain to the caller (so it can handle errors or return the chain to its caller). await doesn't handle errors, so it has to return the chain to the caller. The way it does that is that an async function always returns a promise, and that promise is chained to the promise await awaits.
That is, this:
async function foo() {
const thingy = await somethingAsyncReturningAPromise();
return thingy.foo;
}
is conceptually (but not literally) this:
function foo() {
return somethingAsyncReturningAPromise()
.then(thingy => thingy.foo);
}
If something goes wrong in somethingAsyncReturningAPromise, the promise returned by foo rejects — the error is propagated to the caller.
As far as I can tell from the top-level await proposal, it simply allows unhandled rejections at the top level of the module to be unhandled rejections. So just like this code causes an unhandled error:
null.doSomething();
this code in an async module would cause an unhandled rejection:
await somethingThatReturnsAPromiseAndRejects();
Why should we have an async function in order to use await? Why can't we just use await without async?
Because async/await is "just" syntactic sugar for Promises. If the function is async, then it returns a Promise. It is not possible to have the "await" behaviour without returning a promise. The fact that the function is async has to be marked explicitly.
JS is async by default too, this just adds to the confusion.
This statement is too "simplified". While it is true that JS is async in nature, because of the event loop, this doesn't mean that every function has an async behavior. This does not add to the confusion. You're probably confused due to misunderstanding how JS really works. You should read about Promises, which are behind the scenes when you see async/await.
JavaScript has task based concurrency. It basically means that code blocks (tasks) runs synchronously without being interrupted, until it reaches a certain breakpoint (the end of a task). That has some advantages:
1) You do have concurrency, e.g. a network call does not block your script from doing other things in the meantime (so the task that consumes the network request will only run if the network request was done, other tasks can be done in the meantime).
2) On the other hand, you do not have concurrent mutations, which eliminates a lot of problems (let a = 1; a += 1; could evaluate to 3, you would need locks / semaphores to prevent those, c.f. Java & others).
Now async / await allow you to define such tasks:
An async function can be divided into tasks, await serves as a breakpoint:
let a = 1;
async function stuff() {
a = a + 1; // this is totally secure, as no other code might run in the meantime
a = a + await other(); // this is unsafe, as we await, which means that other tasks might be executed in the meantime.
}
If you want to "await in non async functions" that basically means that you won't know wether a certain function call runs synchronously (meaning: without other code running in the meantime) or not:
function dangerous() { await stuff(); }
let a = 1;
a = a + dangerous(); // does that work or not? Can a be manipulated in the meantime?
So with your proposal you would basically remove the borders around tasks, every code might run every inbetween. So at the end that causes chaos, and chaos is not good if you want to be productive.

What is the meaning of the `async` keyword?

I have been reading up on async/await in Node.js. I have learnt that the await keyword waits for a promise to be resolved, or throws an exception if it was rejected.
I have also learnt that every function that wants to use await needs to be marked async. However, what does it mean for a function to be marked async?
All the resources and blog posts I was able to find seem to explain await in great detail, but ignore the concept of an async function, or briefly gloss over it. For instance, this author puts it like this:
This makes the function return a Promise implicitly.
What does the async keyword really do? What does it mean for a function to implicitly return a Promise? What are the side effects other than being able to use await?
Alright, so from the answers I have received so far it's clear that it simply wraps the function's return value into a Promise, much like Promise.then would. That just leaves a new question though. Why does a function that uses await need to be async and thus return a Promise?
No matter what you actually return from your function, your async function will still return a Promise. If you return a Number, it actually returns a Promise that resolves to the Number your originally returned. This allows you to write synchronous "looking" code.
Rather than having to write out this:
function foo(){
return Promise.resolve("foo");
}
You can just write this:
async function foo(){
return "foo";
}
and foo() will automagically return a Promise that resolves to "foo".
In response to you comment:
Does it behave like Promise.then in the sense that if you already
return a Promise, it won't wrap it again?
await will peel the Promise until it gets to a value:
async function foo() {
return Promise.resolve(Promise.resolve(true));
}
async function bar() {
return true;
}
(async function () {
let tmp;
tmp = await foo();
console.log(tmp);
tmp = await bar();
console.log(tmp);
console.log("Done");
}());
/*
Prints:
true
true
Done
*/
Why is async needed?
Paraphrasing from #KevinB's comment.
await, just like yield in a generator, pauses the execution of that context until the Promise it's waiting on is no longer pending. This cannot happen in normal functions.
If a function is async but does not contain an await, the promise will be resolved immediately, and any callbacks will be ran on the next tick.
What does async do?
async is syntactic sugar for making your method chain Promise objects.
Take the following method for example:
async function myFunction(a)
{
if (a == 10)
{
await otherFunction();
}
return 10;
}
The JavaScript runtime you use might make more optimized code, but in its simplest it will be something along the lines:
function myFunction(a)
{
if (a === 10)
{
return otherFunction()
.then(() => myFunction_continuation());
}
else
{
return myFunction_continuation();
}
function myFunction_continuation()
{
return Promise.resolve(10);
}
}
For documentation on the Promise type I recommend checking out the Mozilla Developer Network page about the Promise type .
Why do you need to mark it async? Why not just use await?
Because your method needs to be split up into multiple "parts" for it to be able to have code execute after the method being awaited on. The example above has a single continuation, but there could be multiple.
The designers of JavaScript want to make it visible to the developer that the runtime is doing this "magic". But maybe the most important reason is that they don't want to break existing code using await as a variable name. They do this by making await a "contextual keyword". A "contextual keyword" is only a keyword in specific scenarios, in this case: when used inside a method marked as async:
function ABC()
{
var await = 10;
}
The above compiles. But if I add the async keyword to the function declaration it no longer does and throws an Uncaught SyntaxError: Unexpected reserved word.
Asynchronous Task Running
The Basic idea is to use a function marked with async instead of a generator and use await instead of yield when calling a function, such as:
(async function() {
let contents = await readFile('config.json');
doSomethingWith(contents);
console.log('Done');
});
The Async Keyword before function indicates that the function is meant to run in an asynchronous manner. The await keyword signals that the function call to readFile('config.json') should return a promise, and if it doesn't, the response should be wrapped in a promise.
The end result is that you can write asynchronous code as if it were synchronous without overhead of managing an iterator-based state machine.
Understanding ECMACSCRIPT 6 by Nicholas c. Zakas

Is it legitimate to omit the 'await' in some cases?

I am using async/await in several places in my code.
For example, if I have this function:
async function func(x) {
...
return y;
}
Then I always call it as follows:
async function func2(x) {
let y = await func(x);
...
}
I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
For example:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
Is this conclusion correct, or else, what am I missing here?
EDIT:
A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:
It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.
For example, removing the await in the code below is "dangerous":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.
If func is an async function then calling it with and without await has different effects.
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).
func(1).then(z => /* use z here */)
If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.
await just lets you to treat promises as values, when used inside an async function.
On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).
So:
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.
So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.
So yes, is always safe to drop await in the last return statement.
PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.
PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.
An async function always returns a Promise.
So please keep in mind that these writing of an async function are all the same:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.
As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.
Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.
Compare these two examples within the scope of an async function:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
with:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.
In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.
(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)
I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.
I got a good answer above, here is just another explanation which has occurred to me.
Suppose I have this:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
The reason why I can safely remove the await in the return statement on func2, is that I already have an await when I call func2 in func3.
So essentially, in func3 above I have something like await await func(x).
Of course, there is no harm in that, so it's probably better to keep the await in order to ensure desired operation.

Categories