Why are await and async valid variable names?
Reserved keywords cannot be used as identifiers (variable names). Unlike most other special Javascript words (like those listed in the question, let
, finally
, ...), await
is not a reserved keyword, so using it as a variable name does not throw a SyntaxError. Why wasn't it made into a reserved keyword when the new syntax came out?
Backwards compatibility
Back in 2011, when ES5 was still a relatively new thing, code that usedawait
(and async
) as variable names was perfectly valid, so you may have seen something like this on a couple sites:function timeout(ms) {
var await = $.Deferred();
setTimeout(await.resolve, ms);
return await.promise();
};
The choice of that variable name may seem odd, but there was nothing wrong with it. await
and async
have never been reserved keywords - if the writers of the ES2017 specification made await
into a reserved keyword, and browsers implemented that change, people visiting those older sites on newer browsers would not be able to use those sites; they would likely be broken.So perhaps if they were made into reserved keywords, a few sites which chose a peculiar variable name wouldn't work properly - why should the existence of those sites permanently affect the future evolution of ECMAscript and result in confusing code like in the question?
Because browsers will refuse to implement a feature which breaks existing sites. If a user finds that a site does not work on one browser, but works on another, that will incentivize them to switch browsers - the maker of the first browser would not want that, because that would mean less market share for them, even if it's a feature which makes the language more consistent and understandable. In addition, the editors of the specification do not want to add something that will never be implemented (or will only be implemented sporadically), because then the specification would lose some of its status as a standard - contrary to its main goal.
You could see these interactions in action with Array.prototype.flatten
and Array.prototype.contains
- when browsers started shipping them, it was found that they broke a few existing sites due to name conflicts, so the browsers backed out of the implementation, and the specification had to be tweaked (the methods were renamed to .flat
and .includes
).
There actually is a situation in which
await
cannot be used as an identifier, which is inside of ES6 modules:<script type="module">
const await = 'Does it work?';
</script>
Why does this function call throws await is only valid in async function Syntax Error even though the function is async?
your problem is your await
here:
let indexJSON = await loadJson(url)
indexJSON.then(() => {
// some code
})
if you want the promise call the function without await
:let indexJSON = loadJson(url)
indexJSON.then(...)
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?
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 forsomeOtherAsyncFunc()
.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) Javascript function await is only valid in async functions
Your issue is you are missing the async
keyword in some functions, because whenever you use await
it is mandatory to be used the async keyword
An async function is a function declared with the async keyword, and
the await keyword is permitted within it. The async and await keywords
enable asynchronous, promise-based behavior to be written in a cleaner
style, avoiding the need to explicitly configure promise chains.
async function readFile(fileName) {
console.log({ fileName });
return await Tesseract.recognize(fileName, 'ara', {
logger: m => console.log(m)
});
}
async function parseDataFromTextAndPropertyNames(text, propertyNames) {
console.log({ text, propertyNames });
return propertyNames
.reduce((table, key) =>
Object.assign(table, {
[ key ]: RegExp(`${ key }\\W+(\\w+)`)
.exec(text)?.[1] ?? ''
}), {});
}
async function start(filenames) {
const { data: { text } } = await readFile(filenames);
const data = await
parseDataFromTextAndPropertyNames(text, ['نقطة الخدمة', 'رقم العداد']);
document.getElementById("dvjson").innerHTML = JSON.stringify(data, undefined, 4);
const final = [JSON.stringify(data)];
const ff = [JSON.parse(final)];
constructTable('#table',ff);
htmlstring = getElementById('table').innerHTML();
$.post('change.php', {string:htmlstring}).done(function(response){
alert("done");
});
console.log({ result });
}
Different behavior of async functions when assigning temporary to variable
The answer from Jonas Wilms is absolutely correct. I just want to expand upon it with some clarification, as there are two key things one needs to understand:
Async functions are actually partially synchronous
This, I think, is the most important thing. Here is the thing - knowledge of async functions 101:- They will execute later.
- They return a Promise.
await
keyword followed by a Promise, and then pause, wait until the Promise is resolved and continue:function getValue() {
return 42;
}
async function notReallyAsync() {
console.log("-- function start --");
const result = getValue();
console.log("-- function end --");
return result;
}
console.log("- script start -");
notReallyAsync()
.then(res => console.log(res));
console.log("- script end -");
Why is async required to call await inside a JavaScript function body?
There are three reasons the async
keyword exists:
In ECMAScript language versions prior to 2015,
await
was not a keyword. Marking a functionasync
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 theawait
keyword as a variable (in fact this was done intentionally in some cases as a polyfill beforeasync
/await
was standardized), since that would cause a breaking change without the addition ofasync
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 theawait
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 offunction bar()
:
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, reject) => {
try {
resolve((/*async function bar*/() => {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
})());
} catch (reason) {
reject(reason);
}
});
}function bar() {
return new Promise((resolve) => {
if (Math.random() < 0.5) {
return resolve('return');
} else {
throw 'throw';
}
});
}
Save Async/Await response on a variable
1) Return something from your asyncExample
function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise
:;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:- You can't do top-level
await
(there's a proposal for it though);await
must exist within anasync
function.
async/await
at all; Since
axios
already returns a Promise
you can simply do:const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
Why await an async function directly not work without assigning it to a Task variable
This is because when you are returning a running Task
when you call Sleep()
even when you're assigning to a variable.
The confusion is that the Task
does not begin if you assign it to a variable (A
, B
, or C
) until you call await A;
but that's not true. As soon as you assign sleep();
to A
, sleep()
was called; therefore the Task
in the sleep()
method is running. Assigning it to a variable or not the Task
begins when you call the method; because in the method you start the Task
.
Knowing this; when you call:
await A;
await B;
await C;
A, B, and C, have already starting simultaneously... After awaiting A it is most likely B, and C have also completed or are milliseconds from completing.There are situations where you can reference a Task
that hasn't started yet but you would have to purposely return a non-running Task
to do that.
To answer the edit to your question also.
Task
s have a method called GetAwaiter()
which returns a TaskAwaiter
. In C# when you write var task = sleep();
then you're assigning the actual Task
to the task variable. All the same when you write await sleep();
the compiler does some cool stuff and it actually calls the Task.GetAwaiter()
method; which is subscribed to. The Task
will run and when it is complete the TaskAwaiter
fires the continuation action. This can't be explained in a simple answer but to know the outer logic helps.
Among other things the TaskAwaiter
implements ICriticalNotifyCompletion
which in turn implements INotifyCompletion
. Both have one method each, OnCompleted(Action)
and UnsafeOnCompleted(Action)
(you can guess which is which by naming convention).
Another thing to note is that Task.GetAwaiter()
returns a TaskAwaiter
but Task<TResult>.GetAwaiter()
returns a TaskAwaiter<TResult>
. There's not a strong difference in the two but there is a difference in the GetResult()
method of the two tasks; which is what's called while marshalling back to the proper threading context. The TaskAwaiter.GetResult()
returns void and the TaskAwaiter<TResult>.GetResult()
returns TResult
.
I feel like if I push further into this I'll have to write pages to explain it all in detail... Hopefully just explaining your question and pulling the curtain back a little bit will shed enough light to help you both understand and dig deeper if you're more curious.
Ok, so based on the comment below I want to describe my answer a little bit further.
I'll start this simple; let's just make a Task
; one that isn't running, and look at it first.
public Task GetTask()
{
var task = new Task(() => { /*some work to be done*/ });
//Now we have a reference to a non-running task.
return task;
}
We can now call code like:public async void DoWork()
{
await GetTask();
}
… but we'll be waiting forever; until the application ends, because the Task
was never started. However; we could do something like this:public async void DoWork()
{
var task = GetTask();
task.Start();
await task;
}
… and it will await the running Task
and continue once the Task
is complete.Knowing this you can make as many calls to GetTask()
as you like and you'll only be referencing Task
s that have not started.
In your code it's just the opposite, which is fine, as this is the most used way. I encourage you to make sure your method names notify the user of how you're returning the Task
. If the Task
is already running the most common convention is the end the method name with Async
. Here's another example doing it with a running Task
for clarity.
public Task DoTaskAsync()
{
var task = Task.Run(() => { /*some work to be done*/ });
//Now we have a reference to a task that's already running.
return task;
}
And now we will most likely call this method like:public async void DoWork()
{
await DoTaskAsync();
}
However; note that if we simply want to reference the Task
just like we did earlier, we can, the only difference is this Task
is running where the one prior was not. So this code is valid.public async void DoWork()
{
var task = DoTaskAsync();
await task;
}
The big take away is how C# handles the async / await keywords. async
tells the compiler that the method is going to become a continuation of a Task
. In short; the compiler knows to look for all await
calls and put the rest of the method in a continuation. The await
keyword tells the compiler to call the Task.GetAwaiter()
method on the Task
( and basically subscribe to the INotifyCompletion
and ICriticalNotifyCompletion
) to signal the continuation in the method.
And this I wanted to add just incase you weren't aware. If you do have more than one task that you want to await but would rather await one task as if they were all one then you can do that with
Task.WhenAll()
So instead of:var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await taskA;
await taskB;
await taskC;
You could write it a little cleaner like so:var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await Task.WhenAll(taskA, taskB, taskC);
And there are more ways of doing this sort of thing built in; just explore it.
Related Topics
Google Map API - Multiple Icons in Wrong Spot
How to 'Await' on an Rx Observable
Why Duck Typing Is Allowed for Classes in Typescript
JavaScript .Replace Only Replaces First Match
Why Are Exceptions Used for Rejecting Promises in Js
Fix the Upstream Dependency Conflict Installing Npm Packages
JavaScript Ternary Operator Example with Functions
How to Update a Specific Index from the Array in Firestore
Render Object Properties in React
Why Is 'This' Undefined Inside Class Method When Using Promises
React Doesn't Reload Component Data on Route Param Change or Query Change
Detect 64-Bit or 32-Bit Windows from User Agent or JavaScript
Localstorage Object Is Undefined in Ie
Why Doesn't Nodelist Have Foreach
How to Replace Last Occurrence of Characters in a String Using JavaScript