I am very new to the concept of asyncronicity in Javascript and I wanted to make sure I am not misinterpreting what I am reading.
Consider this pseudo code in some Angular app:
async ngOnInit() {
this.responseObject = await this.myService.myGetRequest();
//do some more stuff down here
}
My understanding is that the ngOnInit() will 'pause' or stop execution at await, and NOT execute the code below that line until the promise object(data) is returned? Correct?
await used within an async function awaits the fulfillment of a Promise value or converts the variable to a Promise.
Yes, you are correct the code at next line will not be executed until the previous line which uses await has returned a fulfilled Promise or the value is converted to a Promise.
Note, it is not clear what the pattern
await this.responseObject = await this.myService.myGetRequest();
is expected to achieve. The first await should be able to be omitted
this.responseObject = await this.myService.myGetRequest();
The async function declaration will return a Promise that is resolved with the returned valued from your function call.
If you add the await expression, it will stop the async execution and wait until your promise is resolve to continue executing the other instructions in your code, somehow making it behave like a 'synchronous' function.
Do some reading over here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
I feel the two above answers don't answer the core question. What does javascript mean when it says it's awaiting something? Javascript is singly threaded. So, instead of dealing with async itself, it runs all tasks in a separate task queue provided by the "runtime" (usually either the browser or node). When we await a piece of code, we are placing the promise on the task queue, waiting on the promise to finish, and then taking it off the task queue fo code execution to finish. For example, consider the following code block.
setTimeout(() => console.log("Finished Outer Timeout"), 0);
async function asyncCall() {
console.log("Before Await")
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Finished Inner Timeout")
resolve('resolved');
}, 2000);
});
}
await resolveAfter2Seconds()
console.log(`After Await`);
}
asyncCall()
If await fully blocked the task queue, we would expect the logging order to be:
Before Await
After Await
Finished Outer Timeout
Finished Inner Timeout
as the timeouts would have to wait on the task queue for the main script to finish. However, the ordering is:
Before Await
Finished Outer Timeout
Finished Inner Timeout
After Await
which indicates that the task queue can still run the two timeout functions even when the main script is awaiting. Note there may be differences depending on your browser, but this is generally how Javascript async/await works.
Work Cited:
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
https://medium.com/#sadia.islam.badhon/callback-queue-vs-microtask-queue-9dc6a790330e
https://html.spec.whatwg.org/multipage/webappapis.html#queuing-tasks
Related
This question already has answers here:
Javascript async and await
(3 answers)
Closed 4 months ago.
I'm learning promises and I came across this piece of code
async function main() {
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("hello");
resolve();
}, 1000);
});
console.log("world");
}
main();
The output this produces is "hello world".
What I'm not able to understand is why the inner console log "console.log("world")" is waiting for the promise to resolve.
Am I missing something here, it would be nice if I can get some help understanding this or maybe some link to some documentation for further reading.
Thanks in advance.
Am I missing something here
Bluntly speaking yes, you're missing first word of the second line.
The key to understand what's happen there is await.
You're creating a promise with
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("hello");
resolve();
}, 1000);
});
This promise will start an asynchronous action, that could end somewhere in the future (after 1 sec in this particular case).
You probably supposed that this action should be executed after console.log("world"); and it could happen if there was no await before promise.
But we do have this await so we litteraly said to program "Hey, I want to wait for this action to end, before I'll go farther".
That's why we see printed hello before world.
Await expressions make promise-returning functions behave as though
they're synchronous by suspending execution until the returned promise
is fulfilled or rejected.
Source
Let's break this down step-by-step without overcomplicating things. This is what is happening inside your main() function:
the first line is executed as expected, but the keyword await inside your function stops the progress (the promise must be first fulfilled or rejected)
inside the promise you have setTimout(), another asynchronous function, but this one is not awaited;
setTimeout() is an asynchronous function, meaning that the timer
function will not pause execution of other functions in the functions
stack. In other words, you cannot use setTimeout() to create a "pause"
before the next function in the function stack fires.
source
Yet, the execution seems to be paused, but it is not due to setTimeout() function on its own. It is because the promise waits to be resolved. And when do we resolve it? After 1 second.
The callback inside setTimeout() is executed as expected: console.log('hello') is printed first and the promise is resolved right after with resolve().
With the promise being resolved, the progress finally continues with the console.log('world').
Thus we end up with
// hello
// world
You can try moving the resolve() outside of the setTimeout() and observe how the promise is resolved right away and the printed message is reversed (because, as I said above, setTimout() does not pause the execution of the promise on its own).
async function main() {
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('hello')
}, 1000)
resolve()
})
console.log('world')
}
// world
// hello
To wrap it up:
What I'm not able to understand is why the inner console log >"console.log("world")" is waiting for the promise to resolve.
The console.log("world") is not waiting for the promise, it is waiting for the delay inside setTimeout(). Just as promise is waiting to be resolved: which happens right after the console.log("world").
Because this is how promises work. They are designed to always return a clear result. Read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise for more info.
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()
I have the following code:
const doWorkPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('#2 (async call)');
}, 0);
resolve('#3 (async call?)');
console.log('#1 (sync call)');
});
doWorkPromise.then((result) => {
console.log('Success!', result);
}).catch((error) => {
console.log('Error!', error);
});
The output is:
#1 (sync call)
Success! #3 (async call?)
#2 (async call)
I know that sync code lands on the call stack and the async code registers event-callback pair at the Node APIs, where the callback is moved to the callback queue after the event. The event loop is then going to take elements out of the callback queue only when the call stack is empty. Therefore, I expected to have the sync code executed first and then the async one in order, because the timeouts are 0s, like:
#1 (sync call)
#2 (async call)
Success! #3 (async call?)
Can someone please explain why resolve is executed before '#2 (async call)'?
reject is never executed.
resolved is executed before #2 because:
setTimeout puts a function on the time out queue
resolve resolves the promise
console.log logs #1
The function finished
doWorkPromise.then assigns a function to run when the promise resolves
The promise is already resolved so that function is executed
After the minimum timeout time passes the function passed to setTimeout is called and logs #2
Your code executes the following synchronously:
resolve('#3 (async call?)');
...so that promise gets immediately in a resolved state. The fact that you have started a timer, as no bearing on the promise... it is unrelated.
So the then callback on that promise will be triggered on the next processing of the job queue, and so you'll see "Success", right after the execution of the main, synchronous code has completed.
Somewhat later (yes, even setTimeout(..., 0) will introduce a small delay), the timer will expire, but that event has nothing to do with any promise.
The common pattern
When you create a promise with new Promise and want it to resolve some time later, then you could indeed use setTimeout, but then the idea is to call resolve only when that timer has expired. Otherwise the timer does not do anything that serves the promise:
setTimeout(() => {
resolve();
}, 0);
lets consider we have N async functions like:
async function1(){
return await fetch(...);
}
.
.
.
async functionN(){
return await fetch(...);
}
then we have a function like:
async wrapper(){
let var = await fetch(...);
function1();
.
.
.
functionN();
}
would this create one big microtask queue that would effectively block ui thread going to next task before all called functions resolved their awaits?
There is nothing in the microtask queue until promises resolve. Until then other tasks and (UI) events can be processed.
This is because the await operator will make the corresponding async function return immediately, allowing other JS code to execute. In your case the promise is returned by fetch, which in practice will not resolve immediately. So there is nothing blocking here.
Then when the HTTP response makes the fetch promise resolve, a microtask will indeed be created, which, when when executed will restore the corresponding async function's execution context. Your example function has nothing else to do, so that is quickly done.
Note that it does not matter whether this function was originally called from within some other function: at this stage, only that particular function's execution context (in which an awaited promise resolved) is restored without any pre-existing callstack. So it does not return again to the wrapping function. That already happened in the first phase and will not happen again.
Then again there is free event processing until the next fetch promise resolves. And so it continues.
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