Why Are Await and Async Valid Variable Names

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 used await (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 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)

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:

  1. They will execute later.
  2. They return a Promise.

But point one is actually wrong. Async functions will run synchronously until they encounter an 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:

  1. 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.

  2. 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.

  3. 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';
    }
    });
    }

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 an async function.

However I must point out that your original example doesn't need 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.

Tasks 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 Tasks 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



Leave a reply



Submit