Difference between JavaScript and Python Promise / Awaitable Evaluation - javascript

I've been trying to learn the JS concurrency model given a background in Python's.
Running the following in Python:
async def myFunction():
print("abc")
myFunction()
will print nothing. Running the following in JavaScript:
async function myFunction() {
console.log("abc")
}
myFunction()
will print "abc". In both cases I did not await myFunction, yet the evaluations were different.
I understand why the Python program prints nothing. myFunction is a coroutine function that resolves to nothing, more specifically it returns an Awaitable[None]. But to actually have this awaitable's side effect executed, I must await it.
I have also read Are promises lazily evaluated? with an answer of no, talking about how the eager evaluation of promises is guaranteed.
Even though I've looked at both concurrency models separately, their difference is still confusing. While general clarity about the contrast here would be very helpful, I also do have a specific question: Is there ever a point to awaiting a Promise in JavaScript that resolves to nothing, and should only execute for its side effect? In other words, do await myFunction() and myFunction() possibly have a difference in behavior in a different case, even though they both gave the same output here?

async def myFunction():
print("abc")
awaitable = myFunction()
In Python, myFunction() returns an awaitable. The code hasn't run yet. It only runs when you execute a method (.send()) on the awaitable:
awaitable.send(None)
You typically don't use .send(), these mechanics are hidden. You will just use await myFunction() or asyncio.get_event_loop().run_until_complete(myFunction()), and this will execute the function's code behind the scenes.
In JavaScript, an async function returns a promise. The MDN JS reference says:
Promises in JavaScript represent processes that are already happening
So when you call the async function in JavaScript, the function's code starts running, and at some point a promise is returned. Here's what the MDN JS reference says about it:
The body of an async function can be thought of as being split by zero or more await expressions. Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.
Given all this, I don't see why you would await an async JS function if you only wanted its side effect (unless the caller needs the side effect to have occurred before proceeding).

Inside an async function all the asynchronous operations are done by using an await in front of it.
If there is an await in front of a promise, then the we wait until the async operation is completed and then continue the remaining code execution.
If we don't use await infront of an async operation, we don't resolve the promise and continue.
Await in front of a synchronous operation has no effect.
// Case I
const test = async() => {
let res = fetch("http://localhost:3000");
console.log(res);
console.log("It is working");
// We dont wait for the http request to complete,
// We print res as a <<promise>>
// and continue to the next line and print "It is working".
}
test();
// Case II
const test = async() => {
let res = await fetch("http://localhost:3000");
console.log(res);
console.log("It is working");
// We wait for the http request completion.
// After that we print the result.
// Then print ===> It is working
}
test();
()=> {} // stands for an anonymous function also know as Fat arrow function.
You use await in front of a function only if it returns a promise.
If it returns a promise, we wait for the promise to resolve and continue code execution.
If it doesn't returns a promise, default behavior is expected.
const test = () => {
return fetch("http://localhost:3000");
}
let testingAsynFunc = async() => {
let a = await test();
//Here we wait until the promise is resolved and then continue the code execution.
console.log(a);
console.log("working");
}
testingAsyncFunc()

Related

JS Promises: Why does await have to be inside an async function?

Say I have the following code:
new Promise(res => res(1))
.then(val => console.log(val))
I can achieve the same thing with async/await like this:
let func = async () => {
let val = await new Promise(res => res(1))
console.log (val)
}
func()
I put the async/await code inside a function only because you have to be inside an async function in order to use await.
What I Want To Know: Why is this rule enforced? What would be the problem with just doing
let val = await new Promise(res => res(1))
console.log (val)
Is the reason that await causes the current scope to pause execution, and so forcing you to put the async/await code inside the special scope of an async function prevents JS from pausing the execution of all the rest of your code?
An async function is a different kind of function. It ALWAYS returns a promise. At the point of the first await that it hits, the function execution is suspended, the async function returns that promise and the caller gets the promise and keeps executing whatever code comes next.
In addition, the async function automatically wraps its function body in a try/catch so that any exceptions whether synchronous or unhandled rejected promises from an await are automatically caught by the async function and turned into a rejection of the promise they automatically return.
And, when you return a value from the async function, that return value becomes the resolved value of the promise that it returns.
What I Want To Know: Why is this rule enforced? What would be the problem with just doing...
An async function has a number of behaviors that a regular function doesn't and the JS interpreter wants to know ahead of time which type of function it is so it can execute it properly with the right type of behavior.
I suppose it might have been possible for the interpreter to have discovered when compiling the function body that it contains an await and automatically given the surrounding function an async behavior, but that's not very declarative and simply adding or removing one await could change how the function works entirely. Just my guess here, but the language designers decided it was much better to force an async function to be declared that way, rather than infer its behavior based on the contents of the function body.
The big picture here is to understand that an async function just works differently:
Always returns a promise
Automatically catches exceptions or rejected awaits and turns them into rejections
Suspends function execution upon await
Converts returned value into resolved value of the promise it returns
Chains an explicitly returned promise to the async-returned promise.
And, as such the language is clearer and more declarative if that separate behavior is spelled-out in the declaration with the async keyword rather than inferred from the function body.

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 Does Async Always Return a Promise?

This question is theoretical - I have no concrete problem to solve.
With that said, why does the async keyword wrap the return value of an async function in a promise? What's the point? Is it ONLY because the await expression expects a promise? Or is there some meaning / use behind this decision?
I thought i'd answer this primarily because async in Javascript used to confuse the hell out of me, and all of a sudden it snapped, so i hope this analogy may help this happen for you.
You have an async event. This could be anything, getting something from a server, doing something in the browser that takes time, training a machine learning model (!), executing a function or method that uses a setTimeout etc.
The beauty of Javascript and a key reason it works so well for the browser is that it uses the processor thread it runs on in a very clever way that stops the thread from getting blocked by processes that take time (like the ones mentioned above)
Many other languages, for example Ruby run on more than one thread. It is possible to use service workers to run processes on multiple threads in javascript but that is outside the scope of this answer!
The async nature of the JS event loop allows the thread to 'go off' and do something else while it is waiting for a process to finish.
The problem with this from a programming point of view is that it is possible for something in the code that relies on the result of a blocking event to get 'undefined' as a result of the event if it doesn't wait for the event to finish before it tries to use the result of it. Take this piece of code below
let scopedVariable
console.log('the code has started')
setTimeout(() => {
scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)
When the code reaches the console log, the setTimeout hasn't yet completed. As the setTimeout sets the scopedVariable only when it completes, the variable is undefined when we log it
if however
We wrap the timeout in a promise we can await it's resolve callback (first argument of promise) and the code will 'pause' until the promise reaches the resolve callback before continuing.
When we await the promise and the setTimeout completes, the resolve function sets the variable, so that when we console log it it holds the value from the promise
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
const result = await asyncEvent
console.log(scopedVariable)
}
container()
You can use await and .then interchangably
For example we could go:
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
asyncEvent.then(() => console.log(scopedVariable))
}
container()
once again the code will pause at .then and then continue when the asyncEvent promise has resolved.
In fact if we use .then we don't need to enclose it in an async function so we can rewrite it like this
let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
asyncEvent.then(() => console.log(scopedVariable))
The great thing about .then is that the accompanying .catch allows you to catch any errors thrown by the async event (for example if retrieving something from a server when there is an error). For async await you need to wrap potentially dangerous functions in a try catch.
In order to use await you need to be inside an async function (hence the async container function above). This is not necessary with .then, but .then and .catch chains can make your code messy.
I hope this helps!
The async and await operators are just syntactic sugar that hide the underlying use of promises to implement asynchronous code.
Using async before a function definition makes the function return a promise that resolves to the function's return value, rather than returning normally.
Using await before an asynchronous function call suspends the current function until the promise that it returns is resolved. It's basically equivalent to wrapping the remainder of the function in an anonymous function, and using that as the .then() callback of the promise.
For more information between the relationship, see How to translate Promise code to async await

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