Here is my sample code:
async function counter() {
console.log('1');
let next = new Promise((resolve) => {setTimeout(() => resolve(3), 5000)});
let three = await next;
console.log('2');
console.log(three)
}
counter();
The accepted answer on this question says that
When you execute something synchronously, you wait for it to finish
before moving on to another task. When you execute something
asynchronously, you can move on to another task before it finishes.
What I understand from this is that 2 should be logged to the console immediately since the function is asynchronous and it shouldn't wait for another task (the promise) to finish (resolve).
However, what I see is that both 2 and 3 are logged together in the console after a wait of 5 seconds. Isn't that what a synchronous function would do based on the above text from the accepted answer?
Thanks.
The await expression causes async function execution to pause until a Promise is settled (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
You can read more about Await Here
And you can read more about this acccepted answer here
What happens basically is if you used await inside your async function, it causes to wait for it until promise is resolved. but outside of that function will still run in parallel to this function.
Related
What is the order of execution for code below?
If await makes us wait for "promise" to resolve, but resolve(...) is inside a setTimeout, and a setTimeout only gets executed after the stack is empty... How are we not frozen on await? Are there not still things on the stack when we are "await"ing? Is there some exception to an await-y setTimeout , or am I not understanding the stack?
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // wait until the promise resolves (*)
alert(result); // "done!"
}
f();
await is basically a substitute for .then - it says "do the rest of this stuff only once the Promise has resolved". It does not freeze the interpreter at the point of the await - it only pauses execution flow for the current function until the Promise resolves.
So
// wait until the promise resolves (*)
might be more accurately read as
// pause execution of this function until the promise resolves (*)
Are there not still things on the stack when we are "await"ing?
Yes, f() returns a Promise (from the async function) synchronously while the await promise is being waited on to resolve. Then the script doesn't do anything else, so the call stack lies completely empty until the setTimeout macrotask runs a second later, adding a short task that calls resolve, which queues a microtask that shortly results in done being logged.
There are a number of times during your code in which there is no currently active task.
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();
In javascript, does await block code? For example, let's say we have the below code:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Will "selectUser" wait to run until addUser is finished so that we can select the user that is added?
Also, let's say that we add some code between the awaits that is not a promise, something like this:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
setTimeout(() => console.log('Do something that takes 3 seconds'), 3000);
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Will "selectUser" wait for addUser but not the setTimeout? If so, how would you write the above code to make addUser run first, then setTimeout and then selectUser?
I would also like to add that I have been studying and reading on both stackoverflow and other resources, but I need some clarification.
Will "selectUser" wait to run until addUser is finished so that we can
select the user that is added?
From MDN Docs - await:
The await expression causes async function execution to pause until a
Promise is settled (that is, fulfilled or rejected), and to resume
execution of the async function after fulfillment. When resumed, the
value of the await expression is that of the fulfilled Promise.
Execution of queryDB function will be paused while it waits for the first promise to settle. Second line of code will only execute after first one has completed successfully.
Will "selectUser" wait for addUser but not the setTimeout?
Yes, that is correct.
If so, how would you write the above code to make addUser run first, then
setTimeout and then selectUser?
You could wrap setTimeout in a function that returns a promise and then await that promise to make sure last line executes after first two have completed.
Following is an example of a wrapper function that wraps setTimeout
function waitForTimeout(seconds) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Hello World");
resolve();
}, seconds * 1000);
});
}
Once you have a wrapper function, you can await it as shown below:
async function queryDB() {
const addUser = await promisePool.execute(
"INSERT INTO Users (User) VALUES ('username')"
);
await waitForTimeout(3); // wait for 3 seconds
const selectUser = await promisePool.execute("SELECT User FROM Users")
}
Important note: blocks is probably a bad word here. A better word is it waits. Even better would be probably to just say that operations that are done using await syntax are guaranteed to be executed sequentially.
The Big Picture
The main reason for using asynchronous code in Node is that we don't block the whole app while we wait for some asynchronous operation like a database request, a network request, a file operation etc. - we only block this particular execution context.
Even though Node.js only uses one thread for executing user code, but I/O operations are asynchronous and they're non-blocking.
So imagine you have an endpoint with the code that you presented above, that is linked to some button "Add user" on the front end.
Bill presses the button, you start handling the request, start waiting for addUser operation
At this point John also presses same button. Your code will still be executed and will also start to wait until addUser finishes.
Let's say that Users table has gotten into a deadlock and any db operations with it will be super slow, and we have a third user, Jane, who is just browsing your site but she doesn't need to sign in/sign up, so she doesn't touch Users table. What happens? Jane is totally fine, her requests will go through without any issues.
How to await setTimeout
setTimeout is coming from "previous era" so it uses callback instead of async/await syntax. But it is always possible to convert callback style to async/await, if you need it.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await delay(5000);
In short, await only blocks the code(s) that's after the await statement in the current async function, not the whole thread. Once the await has been resolved the rest of the code gets executed. In your example, since you have 2 await statements, the second one won't run until the first one has finished, because the first one blocks the function that both await statements are inside of. For the second example, since setTimeout() doesn't return a promise and you aren't awaiting it either, the function will treat it as a 'synchronous' operation. That is, it will throw the setTimeout() callback into the call stack till the next event loop and continue executing the next line which is another await statement that will block the function again until it is resolved.
In order to execute all three lines one by one, you'll have to put the setTimeout() into a promise and await it so that JS treats it as an asynchronous operation and 'waits' for it to finish before moving on to the next line.
Will "selectUser" wait to run until addUser is finished so that we can select the user that is added?
Yes.
Will "selectUser" wait for addUser but not the setTimeout? If so, how would you write the above code to make addUser run first, then setTimeout and then selectUser?
"selectUser" will be executed after "addUser", but not the setTimeout callback. If you want to run "selectUser" after setTimeout, you can add it inside the setTimeout callback function. So it should look like:
async function queryDB() {
const addUser = await promisePool.execute("INSERT INTO Users (User) VALUES ('username')")
setTimeout(async () => {
const selectUser = await promisePool.execute("SELECT User FROM Users");
}, 3000);
}
Short answer is yes, it blocks. The purpose of await is, to make sure you have the result available in the next line. So if you needed the value of addUser to make the next query, you would have it.
It was introduced so you don't have to write then() at the end of the first line and put the second call into a callback, which makes the code hard to read.
Why is there no async stacktrace when rethrowing an asynchronous exception? With node 12+, the exception when running the following code:
async function crash() {
try {
await (async () => {throw new Error('dead');})();
} catch (e) {
throw new Error('rethrow');
}
}
async function foo() {
await new Promise(resolve => setTimeout(() => resolve(), 1));
await crash();
}
async function entrypoint() {
try {
await foo();
} catch(e) {
console.log(e.stack);
}
}
entrypoint();
is woefully incomplete:
Error: rethrow
at crash (/async-stackt/crash.js:6:15)
I found a workaround by defining the exception in the beginning of crash(), which yields a much nicer:
Error: rethrow
at crash (/workaround.js:2:17)
at foo (/workaround.js:12:11)
at async entrypoint (/workaround.js:17:9)
This is not optimal, since the error has to be constructed in advance whether it is needed or not, and the stacktrace is somewhat imprecise.
Why is the stack trace incomplete when throwing an error from an async catch block? Is there any workaround or change to the code to get a complete stack trace in the first place?
That stack trace is showing you what's ON the stack at that time. When you make an asynchronous call such as setTimeout(), it runs the setTimeout() which registers a timer that will fire some time in the future and then it continues execution. Since you're using await here, it pauses the execution of foo(), but it continues execution of the code after where foo() was called. Since that's also an await, it continues executing the code that called entrypoint(). After that finishes, the stack is entirely empty.
Then, sometime later, your timer fires and its callback gets called with a completely clean stack. In your case, the setTimeout() callback just calls resolve() which then triggers a promise to schedule its resolve handlers to run on the next event loop tick. That returns back to the system and the stack frame is again empty. On that next tick of the event loop, the promise resolve handlers are called and that satisfies the await on that promise which is inside a function context. When that await is satisfied, the rest of that function starts to execute.
When that function gets to the end of its execution, the interpreter knows that this was a suspended function context. There is no return from the function to happen because that already happened earlier. Instead, since this is an async function, the end of the function execution resolves the promise that this async function returns. Resolving that promise then schedules its resolve handlers to be called on the next tick of the event loop and then it returns control back to the system. The stack frame is again empty. On that next tick of the event loop, it calls the resolve handlers which satisifies the await in the await foo() statement and the entrypoint() function can continue to run, picking up where it was last suspended.
So, the key here is that when the timer goes off, execution goes from foo back to entrypoint, not via a stack and a return statement (that function already returned awhile ago), but via promises getting resolved. So, at the time the timer goes off and you then call crash(), the stack is indeed empty except for the function call to crash() itself.
This concept of an empty stack when promises are resolved goes to the heart of how an async function actually works so it's important to understand that. You have to remember that it pauses the internal execution of the function containing the await, but as soon as it hits the first await, it immediately causes the function to return a promise and the the callers continue further execution. The caller is not paused unless they also so an await in which case the callers of the caller continuing executing. At some point, somebody gets to continue executing and eventually, it returns control back to the system with a now-empty stack.
The timer event (or some other promise triggering event) then gets called with a completely empty stack frame with no remnants from the original call sequence.
Unfortunately, the only work-around I'm aware of now is to do what you found - create the Error object earlier when the original stack is still alive. If I remember correctly, there is a discussion about adding some features to the Javascript language to make asynchronous tracing easier. I don't remember the details of the proposal, but perhaps by remembering what the stack frame was when the function was originally called since what it is after the promise is resolved/rejected and when the Error object is created is no longer very useful.
In case anyone was unfamiliar with how async functions work and how they suspend their own execution upon the first await, but then return early, here's a little demo:
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
async function stepA() {
console.log("5");
await stepB();
console.log("6");
}
async function stepB() {
console.log("3");
await delay(50);
console.log("4");
}
console.log("1");
stepA();
console.log("2");
This generates the following output. If you follow this execution path step-by-step, you will see how each await causes an early return from that function and can then see how the stack frame will be empty once the promise that is being awaited gets resolved. This is the output generated:
1
5
3
2
4
6
It's clear why 1 is first as it's the first thing to execute.
Then, it should be clear why 5 comes next when stepA() is first called.
Then, stepA calls stepB() so as it begins to execute, that's why we see 3 next.
Then, stepB calls await delay(50). That executes delay(50) which starts a timer and then immediately returns a promise that is hooked to that timer. It then hits the await and it stops execution of stepB.
When stepB hit that await, it causes stepB at that point to return a promise that comes from the function being async. That promise will be hooked to stepB execution eventually (in the future) getting a chance to finish all of its execution. For now the execution of stepB is suspended.
When stepB returns its promise, that goes back to where stepA executed await stepB();. Now that stepB() has returned (an unfulfilled promise), then stepA hits its await on that unfulfilled promise. That suspends the execution of stepA and it returns a promise at that point.
So, now that the original function call to stepA() has returned (an unfulfilled promise) and there is no await on that function call, that top level code after that function call continues to execute and we see the console output the 2.
That console.log("2") is that last statement to execute here so control is returned back to the interpreter. At this point, the stack frame is completely empty.
Then, sometime later, the timer fires. That inserts an event in the JS event queue. When the JS interpreter is free, it picks up that event and calls the timer callback associated with that event. This does only one thing (call resolve() on a promise) and then returns. Calling resolve on that promise schedules that promise to trigger it's .then() handlers on the next tick of the event loop. When that happens, the await on the line of code await delay(50); gets satisfied and execution of that function resumes. We then see the 4 in the console as the last line of stepB executes.
After the console.log("4"); executes, stepB has now finished executing and it can resolve it's async promise (the one returned by it earlier). Resolving that promise tells it to schedule its .then() handlers for the next tick of the event loop. Control goes back to the JS interpreter.
On the next tick of the event loop, the .then() handlers notify the await in the await stepB(); that the promise has now been resolved and execution of stepA continues and now we see 6 in the console. That is the last line of stepA to execute to it can resolve its async promise and return control back to the system.
As it turns out, there is nobody listening to the async promise that the call to stepA() returned so there is no further execution.
I ran into the same issue in Node 12 and found out that it was fixed in a later version of V8: commit.
Gotta wait for Node 14, I guess...
The bug had to do with the stack trace not being tracked in catch blocks. So one workaround is to use the Promise's .catch function instead:
async function crash() {
await (async () => {throw new Error('dead');})()
.catch(e => { throw new Error('rethrow'); });
}
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.).