This question already has answers here:
Async await - does await block other code from running?
(5 answers)
Closed 2 years ago.
I am a beginner in JS and while going through async and await and I came across the below example:
const get = async () => {
const y = await "hello";
console.log(y);
}
console.log("start");
get();
console.log("end");
O/P
start
end
hello
But according to my understanding await blocks the execution of the current program until the promise is available, then why in the above example that doesn't happen? Is my understanding incorrect or some other concept is missing?
When an async function hits the first await in the function, it immediately suspends further execution of that function and then immediately returns a promise from the function. The caller receives that promise and continues to execute. So, the current function is suspended, but not the caller or the rest of the JS engine.
So, in your code:
const get=async ()=>{
const y=await "hello"
console.log(y)
}
console.log("start")
get()
console.log("end")
Here's the sequence of events:
Logs "start"
Starts to execute get()
Gets to the first await
Suspends further execution of the get() function
Returns promise from get()
Calling code continues to execute and logs "end"
When calling code returns back to the event loop, the await finishes because there was no actual promise there to wait for and the get() function resumes executing and it logs "hello"
So, await only suspends execution of the current function, causing it to immediately return a promise and the calling code then continues to execute. Sometime later when the statement you are awaiting resolves (usually a promise) and the interpreter has returned back to the event loop, then the function will continue its execution until either the next await or until it gets to its return value which then becomes the resolved value of the promise it originally returned and that promise gets resolved (which your code was ignoring).
Note, await should generally be used with promises, not with constants. Though it doesn't cause an error to await something that isn't a promise, it doesn't do anything useful.
await is not blocking the execution of the program, it defers the continuation of the currently executed async method to a microtask and continues executing the code of the async method caller.
When you await something that is not a Promise(string, number or whatever) it will automatically be wrapped in a Promise and that promise will be awaited.
So yes, everything works as expected in your code sample.
First of all async/await works with promises https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
not with strings.
If you want your code to work, here is an example =)
const helloPromise = new Promise((resolve) => resolve('hello'));
const get = async () => {
const y = await helloPromise;
console.log(y);
}
const main = async () => {
console.log("start");
await get();
console.log("end");
}
main();
Related
I have this piece of code and I'm struggling to understand why the log order is "1,3,2".
(async () => {
const doSomething = async () => {
console.log("1");
};
await doSomething();
console.log("2");
})();
console.log("3");
Async/await is implemented under the hood with promises. With promises even if a promise resolves immediately the callback within the then is added as a microtask, and the current callstack execution continues. Then once the call stack is completed the microtasks are run.
So the way I read the code, the await keyword before the doSomething() function means that the rest of the code in the function after the await will be wrapped in a then call, giving control back to the caller of the function. Thus "3" should be logged first, so the order should be "3,1,2"?
What is the control flow? What actually happens when the javascript translator comes across an await keyword?
the await keyword before the doSomething() function means that the rest of the code in the function after the await will be wrapped in a then call, giving control back to the caller of the function.
Yes:
(() => {
const doSomething = () => {
console.log("1");
return Promise.resolve();
};
return doSomething().then(() => {
console.log("2");
});
})();
console.log("3");
Thus "3" should be logged first
No. Logging "1" is not delayed by any promise.
I've been questionning myself recently on how to reproduce a behaviour of then/catch with an async/await syntax.
With then/catch, I can define a callback which is only executed when the Promise resolves and then continue the execution like so .
function test() {
getUsersFromDB().then(users => console.log(users));
console.log('The rest of the code here continues to execute');
[...]
// Promise resolves and logs the users value
}
For me, with async/await you can have 2 possible behaviours.
1. Wait for the function and block the rest of the execution
async function test() {
const users = await getUsersFromDB();
// Code in here is not executed until promises returns
console.log(users);
}
2. Don't wait for the return value but don't expect you promise to be fulfilled when the rest of the code executes
function test() {
const users = getUsersFromDB();
// May log undefined
console.log(users);
}
Can I reproduce the first use case using async/await ?
Using then is the simplest solution, but you can use an AIIFE:
function test() {
(async () => {
const users = await getUsersFromDB();
console.log(users);
})().catch(console.error);
console.log('The rest of the code here continues to execute');
[...]
// Promise resolves and logs the users value
}
An alternative could only be async do expressions.
So what you need is basically split the code. One piece should be executed in async/await syntax, and another one should be as usual.
First what I want to say, if you do as follows
async function test() {
console.log('The rest of the code here continues to execute');
const users = await getUsersFromDB();
// Code in here is not executed until promises returns
console.log(users);
}
that will do the trick. This may appear a little strange because we just moved the line a bit up, this is not what we wanted to do, but...
The await keyword stops execution of an async function, but we have some code that should continue working at the time when await freezes the function, that means we can't place the code after await, only before.
As far as I understand, the code tahat is indicated as "The rest of the code here continues to execute" can be asynchronous too, so the resulting example will be as follows:
async function test() {
console.log('Some synchronous code');
setImmediate(() => {
console.log('Some asynchronous code');
});
const users = await getUsersFromDB();
// Code in here is not executed until promises returns
console.log(users);
}
I am new to JS and was learning promises. So, let's say we have this code:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
})
As you can see the code above, when promise is invoked, setTimeout is run via callback queue. The question is When setTimeOut is sent to a browser, will JS engine omit .then() and continues running the rest of the code until the promise resolves? Secondly, async/await example:
async function showAvatar() {
// read our JSON
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
// read github user
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
showAvatar();
When showAvatar function is called, JS engine will encounter let response = await fetch('/article/promise-chaining/user.json'); and sends fetch to the browser to handle. The second question is Will JS engine wait until fetch gets resolved or Will JS engine continue executing let user = await response.json(); and the rest of the code inside showAvatar function? If so, how can JS engine handle response.json() since response is not received? Hope you got my point))).
Your first example works like this:
new Promise runs, calling the function you pass it (the executor function) synchronously
Code in the executor function calls setTimeout, passing in a function to call 1000ms later; the browser adds that to its list of pending timer callbacks
new Promise returns the promise
then is called, adding the function you pass into it to the promise's list of fulfillment handlers and creating a new promise (which your code doesn't use, so it gets thrown away).
1000ms or so later, the browser queues a call to the setTimeout callback, which the JavaScript engine picks up and runs
The callback calls the resolve function to fulfill the promise with the value 1
That triggers the promise's fulfillment handlers (asynchronously, but it doesn't really matter for this example), so the handler attached in Step 4 gets called, showing the alert and then returning result * 2 (which is 1 * 2, which is 1). That value is used to fulfill the promise created and thrown away in Step 4.
Will JS engine wait until fetch gets resolved or Will JS engine continue executing let user = await response.json();...
It waits. The async function is suspended at the await in await fetch(/*...*/), waiting for the promise fetch returned to settle. While it's suspended, the main JavaScript thread can do other things. Later, when the promise settles, the function is resumed and either the fulfillment value is assigned to response (if the promise is fulfilled) or an exception will get thrown (if it is rejected).
More generally: async functions are synchronous up until the first await or return in their code. At that point, they return their promise, which is settled later based on the remainder of the async function's code.
In a comment you asked:
when async function is suspended at each await, will async function is removed from the call stack and is put back to the call stack again when the promise being awaited settles?
At a low level, yes; but to make debugging easier, a good, up-to-date JavaScript engine maintains an "async call stack" they use for error traces and such. For instance, if you run this on Chrome...
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function inner() {
await delay(80);
throw new Error("boom");
}
async function outer() {
await inner();
}
function wrapper() {
outer()
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error.stack);
});
}
wrapper();
...the stack looks like this:
Error: boom
at inner (https://stacksnippets.net/js:18:11)
at async outer (https://stacksnippets.net/js:22:5)
Notice the "async" prior to "outer," and also notice that wrapper isn't mentioned anywhere. wrapper is done and has returned, but the async functions were suspended and resumed.
This question already has answers here:
Will async/await block a thread node.js
(6 answers)
Closed 3 years ago.
I have two functions, the first is the main function which has switch statement calling the second which is async. If my second function is async isn't that non-blocking? Would I still have to make the first one async in order for it to be non-blocking, if so why?
Example
exports.funcOne = async (theParam) => { // async ??
switch (theParam) {
case 'hey':
return await funcTwo()
default:
...
}
}
const funcTwo = async () => {
await axios.get...
}
Second function can just return the promise :
exports.funcOne = async (theParam) => { // async ??
switch (theParam) {
case 'hey':
const myData = await funcTwo();
console.log("response from request : ", myData);
//do something with data
default:
...
}
}
const funcTwo = () => {
//axios.get is a promise, so is the return type for funcTwo
return axios.get...
}
Calling an AsyncFunction returns a Promise. As Eric said you could return the Promise in the funcTwo and it doesnt need to be an AsyncFunction because it is secuential and it is in the functionOne thread. So if you return the Promise in the functionTwo, the result of the Promise "axios.get..." will be resolved in "return await funcTwo()" in the functionOne.
In general, all functions that contain await must be async.
That rule might make more sense if we imagine it would not exist, and if you could wait for an asynchronous result in a synchronous function.
First of all, we need to know one important things about functions:
They have to run to completion, or in other words: A regular function runs till it's return and no other code can run at the same time.
If we could await inside of a non-async function that would mean that we would block execution until the awaited promise resolves, as no other code can run in the meantime. As the code that resolves the promise also cannot run, we created a deadlock.
async functions only run to completion till they reach an await. Therefore, when axios.get(...) gets called, a promise gets returned synchronously from it, funcTwo halts execution, returns a promise itself, therefore funcOne also halts execution (thats possible cause it's async). Then the engine can continue with other stuff, and somewhen when the underlying request is done, the promise resolves, funcTwo continues execution which resolves the promise it returned, funcOne continues execution and resolves the returned promise too.
If my second function is async isn't that non-blocking?
That really depends on your definition of non-blocking. In general, every code of JS that executes kind of "blocks the thread" (as only one function can execute at a time), but as most tasks are very small, you won't notice that blocking.
An asnyc function is also blocking, as long as it doesn't halt for a promise to resolve. Then it is in a non-blocking waiting state.
Asynchronous tasks (e.g. doing a network request) also aren't blocking, cause they are handled by the engine (offloaded to other threads or hardware, etc.).
I imported a function from an internal utility library that I am unfamiliar with. There is no documentation for this library and I just assumed it was asynchronous due to its name getUserDetails. I thought it was doing an http request.
I used it in an async function like this
async function getAllUserInfo(event) {
const details = await getUserDetails(event);
// other stuff
}
I was wrong in my assumption. A co-worker pointed out that is was not asynchronous. I ended up changing it, but when I was using it incorrectly it still worked. I was able to await a synchronous function and it returned the correct data.
My question is in regards to how it worked. Does prepending an await on a synchronous function make it resolve on the next tick, or does it return immediately like a synchronous function should?
It worked because await does not require its operand to be a promise! It returns the value of the awaited expression if it is not a promise.
See the documentation for the await operator
The important part is:
[rv] = await expression;
expression: A Promise or any value to wait for.
rv: Returns the fulfilled value of the promise, or the value itself if it's not a Promise.
In your case getUserDetails did not return a promise, but rather some regular user details, so the await expression just returned those details, just as if the operator was not there at all.
However, even though getUserDetails is synchronous, preceding it with await in your async function will give up control to its caller, and the "callback portion" after the await is picked up later. Here is an example script:
function f() {
console.log('In f');
}
async function g() {
console.log('Starting g');
await f();
console.log('Finishing g');
}
console.log('Starting the script')
g();
console.log('Finishing the script')
Notice the output of the script:
$ node asynctest.js
Starting the script
Starting g
In f
Finishing the script
Finishing g
Notice how the await call "paused" g, which was not able to resume until the main block finished! So the await did have an effect. If you did not put the await there, then you would have seen "Finishing g" before "Finishing the script". Try it out!
BTW the reason for the effect is that even though await can be given an expression that does not produce a promise, JS will turn a non-promise operand into a promise immediately resolved to that value. So a promise is still created and the part after await is treated as a callback, which cannot be run until the current execution flow finishes.
If you await a value that is not a promise, it is converted to a resolved promise by using Promise.resolve.
function sync(){
return 1
}
(async ()=>{
const v = await sync(); console.log(v)
})();
(async ()=>{
const v = await Promise.resolve(sync()); console.log(v)
})()