console.log("1");
console.log("2");
async function a() {
for (let i = 0; i < 1000000000; i++) {}
for (let i = 0; i < 1000000000; i++) {}
}
a().then(() => console.log("in then"));
console.log("3!");
This output I want is this
1
2
3!
in then
but the async function behaves synchronously and does not let the 3! print until after the long loops are done executing. I thought if the async keyword is used, it runs the function inside in the background?
I basically want the 2 long loops to run in the background. Just trying to understand async and await.
Thanks.
Edit:
could someone tell me why this also works synchronously??
console.log("2");
function lag(resolve) {
for (let i = 0; i < 1000000000; i++) {}
for (let i = 0; i < 1000000000; i++) {}
console.log("in lag");
return resolve;
}
async function a() {
// console.log("its done");
let a = await new Promise((resolve, reject) => lag(resolve));
}
a().then(() => console.log("in then"));
console.log("3!"); ```
There is nothing about async functions (or promises) that makes anything run in the background. They don't create a new thread or anything like that.
An async function is synchronous until the first await or return. Since your function doesn't have any await or return, it's fully synchronous.
The purpose of promises is to observe the completion of something that's already asynchronous, such as the browser's loading things via HTTP, or a timer. The purpose of async functions is to have syntax for using promises that lets us write the logical flow of the code rather than writing callback functions. Neither of them makes anything asynchronous, or moves things to a different thread.
If you want to run something in the background, you can create a worker thread and exchange information between it and the main thread via messaging. On browsers it's web workers. In Node.js it's the worker threads module.
In your question and in comments on the answer you've talked about how an async function seems to wait for an asynchronous task to complete. And indeed, the logic of the function does. But that's really syntactic sugar for using promises (really, really good syntactic sugar, IMHO). Let's look at an async function:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function example() {
// The function starts out synchronous, so that it can
// start whatever inherently-asynchronous thing it does
// (like calling `fetch`).
console.log("[example] This is synchronous");
// When execution reaches the first `await` or `return`,
// the `async` function returns a promise. The synchronous
// part of its work is now complete.
await delay(10);
// This code runs later, when/if the promise from `delay`
// is settled.
console.log("[example] This is asynchronous, but still on the main thread");
// If you `throw` in an `async` function, it rejects the `async`
// function's promise.
if (Math.random() < 0.5) {
throw new Error("Error thrown by `example`");
}
// The "return" value of an `async` function is the fulfillment
// value of the promise the `async` function returned
return 42;
}
console.log("[top] Calling `example`...");
const promiseFromExample = example();
console.log("[top] Back from calling `example`. Waiting for its promise to settle.");
promiseFromExample
.then((value) => {
console.log(`[top] The promise from \`example\` was fulfilled with the value ${value}`);
})
.catch((reason) => {
console.log(`[top] The promise from \`example\` was rejected with the rejection reason ${reason.message}`);
});
.as-console-wrapper {
max-height: 100% !important;
}
Run that a few times so you see both a fulfillment and a rejection (each run has a 50/50 chance). Nothing in that code runs anywhere but the main thread, it's just that with await, the logic in example can wait for a promise to settle. The promise from delay lets us observe the completion of the timer, then example awaits that completion before continuing its logic.
The key things are:
Promises don't make anything asynchronous¹
Promises and async functions don't move anything off the main thread
async functions always return promises
The code in an async function runs synchronously until the first await or return
The promise from an async function gets fulfilled with the return value of the function, or rejected with any error that occurs in the function
¹ Okay, there's one caveat to that: If you use .then or .catch (or .finally) to hook up a callback to a promise, that callback will always be called asynchronously, even if the promise is already settled.
Related
I've researched many many posts and articles, and documentation. I'm seeking deeper understanding.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
The await expression causes async function execution to pause until a Promise is settled, [...]
let fetchLocation = () => {
//fetches information from satellite system and
//returns a Vector2 of lat long on earth
return new Promise(resolve => {
setTimeout(() => {
resolve({
lat: 73,
long: -24
});
}, 2000);
});
}
//async functions automatically return a promise.
let main = async() => {
//awaits the promises resolution
let result = await fetchLocation();
console.log("I'm the awaited promise result", result)
//immediately returns a promise as promise pending
return result;
}
console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))
Before going down this rabbit hole, I knew async/await was syntactical sugar to make async code look synchronous.
I therefore fully expected to see main return the lat long coordinates. Instead it returns a promise.
A couple questions.
Is the MDN documentation wrong? Does it not actually pause execution? It seems that the await keyword does not actually "pause" function execution... invoking main immediately returns a Promise<pending> object. However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop? (And an async return statement is still synchronous.)
So it seems that await unwraps a promise, and async wraps a promise, but you need to use await within async. I get that await might actually be promise.then() underneath the hood but if they want to make it fully synchronous in appearance, why not have the async function return the resolved promise value when used with await? Otherwise async await seems a bit redundant.
Is the MDN documentation wrong?
No
Does it not actually pause execution?
It pauses the execution of the async function, hands a Promise back to the calling function — because it hasn't reached the point in the async function where it knows what to return — then the execution of the calling function (and the rest of the code) continues.
However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop?
When the event loop stops being busy (running the code that called the async function in the first place and anything else that it picks up in the meantime) and the promise that was being awaited resolves, then the async function is woken up and given the resolved value of the function to continue processing.
why not have the async function return the resolved promise value when used with await?
Because that would block the entire event loop, which is why asynchronous logic (starting with callback style APIs) was introduced into JS in the first place.
Otherwise async await seems a bit redundant.
Consider a situation where you want to request a number of URLs from an API serially (so that you don't shove too many simultaneous requests at the API):
const data = [];
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const response_data = await response.json();
data.push(response_data);
}
If you were to rewrite that without await then you'd probably end up with something recursive to count your way through the loop. It would be significantly more complex.
async/await makes dealing with complex combinations of promises simple, and simple promises trivial.
knew async/await was syntactical sugar to make async code look synchronous.
And it does that inside the async function and only inside it.
It doesn't really pause the function, it just allows you to write the rest of the function code as if it were. But it's really just wrapping the rest of the code of the function into a .then().
E.g.
await foo = someFunc();
console.log(foo);
is executed like
someFunc().then((foo => {
console.log(foo);
});
Since it's really still asynchronous, you can't return the resolved value, because the original calling function returns immediately. But if the calling function is also declared async, it returns a Promise, and the caller can either use await again (so it appears to be synchronous) or use .then().
I'm attempting to get a better grasp on async functions and promises in JS. To do this, I wrote a sample program that has the goal of calling a function that does busy work (purposefully not using async setTimeout as I want to mimic a long-running process) but returns immediately. However, I can't seem to figure out why this isn't working.
test();
async function intense(){
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}
async function test(){
console.log("Print 1");
intense(); // does some busy work for a few seconds
console.log("Print 2"); // want this to print immediately after print 1
}
When I run it, I get:
Print 1
Done with async work
Print 2
And I would like it to be:
Print 1
Print 2
Done with async work
I thought that it would print the latter sequence because I declared the function intense() to be async, so it would return a promise immediately and continue work asynchronously.
I even tried to refactor the intense function to be a promise that resolves immediately, but to no avail.
async function intense(){
return new Promise((resolve)=> {
resolve();
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}, null)
}
What am I missing?
There a are a couple of reasons for what you're seeing:
An async function is synchronous up until its first await or return, so the entire function runs before returning in your case.
Busy-waiting isn't asynchronous.
test needs to use await if it's going to wait for intense to complete before continuing.
Moving something into a promise doesn't take it off the thread. The only way to do that in most JavaScript environments (including browsers) is to use a Worker thread (MDN, Node.js docs — Node.js has had Worker since ~v10.5, and while it's still marked "experimental" the main parts of the API should be fairly stable as they're drawn from the web worker standard API).
It's important to remember that promises don't make anything asynchronous¹, they provide a means of observing the result of something that's already asynchronous.
Here's an example using setTimeout for the asynchronous part that help you understand these better:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function intense(value) {
console.log("intense(" + value + ") - This is synchronous");
await delay(100);
console.log("intense(" + value + ") - This is asynchronous, because it's after `await`");
}
async function test(){
console.log("Print 1");
intense(1); // <== WITHOUT await
console.log("Print 2");
await intense(2); // <== With await
console.log("Print 3");
}
test();
.as-console-wrapper {
max-height: 100% !important;
}
¹ There's one small caveat to that: The handler you pass to then, catch, or finally will always be called asynchronously, even if the promise you're calling them on is already settled. That's literally the only thing that promises actually make asynchronous.
so it would return a promise immediately and continue work asynchronously.
No it would not. The callback passed to the Promise constructor is called immeadiately. What is async is the process of calling resolve or reject later on, and how .then chains get called back somewhen.
However it isnt async in the sense that the code runs on another thread or gets deferred, that won't happen as JS itself is executed in a single thread*.
console.log(1);
const promise = new Promise((resolve, reject) => {
console.log(2); // gets executed immeadiately
});
promise.then(() => console.log(4)); // < Promise resolve asynchronously
console.log(3);
*If you plan to do really "intense" work, it might be benefitial to do that in another thread (see WebWorker in browsers and child_process.spawn for NodeJS).
This is a misunderstanding that is pretty easy to make with javascript. Your function intense() blocks the thread. Putting something in an async function does not change the fact that you only have one thread in javascript. As soon as the interpreted starts running that for loop it's going to use the one thread to run it until it's over. Nothing else will happen until then.
Async functions don't return immediately, they run the body of the code until the hit an await and return a promise. In your example, the entire function will run before it returns.
You can't use this kind of long running process without blocking. That's why node goes out of its way to offload things like i/o access and timers to another thread.
See here for more details: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
If you want to run some long-running code like this asynchronously you will need to spawn a child process: https://nodejs.org/api/child_process.html
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.).
My case:
let waiting = function () {
return new Promise(resolve => {
console.log('awaiting...');
setTimeout(function () {
resolve();
}, 1000)
});
};
let waitingAsync = async function () {
console.log('start...');
await waiting();
console.log('stop...');
};
waitingAsync();
console.log('done...');
There are 2 things I don't understand in the code:
The first:
await waiting();
waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?
The second:
Why couldn't done... message be awaited after completing waitingAsync function?
And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().
If I can await waitingAsync(), done... message would be printed last.
This isn't a function but a value that it returns which is awaited with await statement.
async and normal functions aren't different to the caller. async just returns a promise without returning it explicitly when being called. The result of waitingAsync() call is a promise. The result of waiting() call is a promise, too, so it isn't 'synchronous'.
According to the spec, both promises and non-promises can be awaited. Non-promises are converted to promises with Promise.resolve().
console.log('done...') can't be awaited because it isn't called inside async function. And it doesn't have to be awaited because it doesn't return a promise but undefined. awaiting it would be possible within async function. These await usages are equal and equally useless, all they do is 1 tick delay:
async function ... {
...
await console.log('done...');
}
async function ... {
...
console.log('done...');
await undefined;
}
async function ... {
...
await Promise.resolve(console.log('done...'));
}
A async function does return a promise or, when it uses the await keyword, it must wait for a asynchronous function, being a Promise or another async function. In your code, waiting() is a function that returns a Promise. Then, await waiting() is correct (since it is waiting for a asynchronous function).
An async function is called as another basic functions is called. You may mark a function as async when you desire operate it as an asynchronous function and using the 'await' keyword. The 'done...' is printed because when you call asyncFunction(), it must await until waiting promise is finished. But the program is not stopped and go on showing done.... If you want to wait for it, maybe you can use asyncFunction().then( () => console.log('done...') )
Async keyword used to specify that function will be an instance of AsyncFunction so it will return Promise.
Await is used to wait for a promise resolving inside of async function.
According to a mdn - async function can contain an await expression, that pauses the execution of the async function and waits for the passed promise's resolution, and then resumes the async function's execution and returns the resolved value.
When you await a function, if that function returns a promise, its return value will be treated as the promise then value. If the promise will reject, it will be cast to an Error. If the function call returns something ohter than a thenable, well, await then just doesn't nothing.
In the other hand, when you declare an async function, its return value will be returned as a Promise, and any Error thrown from it will be casted to a rejected Promise.
You can use await only within an async declared function.
that's just about async and awaitare, just automatic casting to promises. You don't actually need code using await and async to be really asynchronous (while, well, it's not really useful).
A quick demonstration:
//Will return the string 'Promise' if called through `await`
function getPromise(){
return Promise.resolve('Promise');
}
//Casted to Promise.reject thrught await
function throwError(){
return Promise.reject('error');
}
function get(){
return 'something simple';
}
async function getAsync() {
var response = await getPromise();
return response;
}
//await will cast the rejected Promise to an error
async function getErrorAsync() {
var response = await throwError();
return response;
}
async function simpleGet(){
return get();
}
async function redundantGet(){
return await get();
}
async function catchTheError(){
try{
await throwError();
}
catch(e){
console.log('an error: ' + e );
}
return 'whatever';
}
getAsync().then( console.log ); //Promise
getErrorAsync().catch( console.log ); //error
simpleGet().then( console.log ); //something simple
redundantGet().then( console.log ); //something simple
catchTheError(); //will log 'an error: error'.
So:
waiting is a synchronous function (because it doesn't have async keyword). So, why can I await a synchronous function?
Because you can. The only thing await does is to resolve promise to real values and errors. You don't actually need the function to return a promise.
Why couldn't done... message be awaited after completing waitingAsync function?
async and await only makes your code to behave as it would be synchronous iside asyncdeclared functions. Your last console.log('done') is outside any async function, so it will be just logged before that function ends, since it is asynchronous.
And main question: waitingAsync is an asynchronous function, why is await keyword not required when calling it? Just waitingAsync() instead of await waitingAsync().
Because async keyword casts values to promises -and allows to use await- and nothing more. In fact, since you can only use await inside async functions... you can't expect async functions to be called through await, you would need infinite asyncfunctions :-D.
Before diving in, it's good to notice a few things.
Any reader of the code snippet
let waiting = function () {
return new Promise(resolve => {
console.log('awaiting...');
setTimeout(function () { resolve(); }, 1000);
});
};
let waitingAsync = async function () {
console.log('start...');
await waiting();
console.log('stop...');
};
waitingAsync();
console.log('done...');
may be mislead to believe that the output will be
start...
awaiting...
stop...
done...
while – as you have already noted – done... gets printed before
stop....
The reason is that waitingAsync(); is a call to an asynchronous function,
while console.log('done...'); is just a normal sequential/synchronous
statement that gets carried out right away.
Question 1:
waiting is a synchronous function (because it doesn't have async
keyword) [?]
Answer:
False. The function waiting is asynchronous – it returns
a Promise.
Question 2:
Why couldn't done... message be awaited after completing waitingAsync
function?
Answer:
Because console.log('done...') is not asynchronous.
(It does not return a Promise.)
Question 3:
And main question: waitingAsync is an asynchronous function, why is
await keyword not required when calling it?
Answer:
Well, in your example waitingAsync does not return any value. -
If it would return a value that you care about, then you would need to await
it to get it.
(Hello world! in my Stack Snippet below.)
Question 4:
If I can await waitingAsync(), [the] done... message would be printed
last [?]
Answer:
That depends on what exactly you mean. – See my Stack Snippet below!
As long as the Done! message is printed within the same callback as the call
await waitingAsync(), the answer is Yes!
But if you put console.log('done...?') after the call to the asynchronous
function that encloses await waitingAsync() then the answer is No!
When running the snippet below, pay attention to the order of the output!
Also notice how it takes 1400 ms for Promise resolved! to show up.
function waiting () {
return new Promise(resolve => {
console.log('awaiting...');
setTimeout(function () {
resolve('Hello world!');
console.log('Promise resolved!');
}, 1400);
});
}
async function waitingAsync () {
console.log('start...');
const toBeReturned = await waiting();
console.log('stop...');
return toBeReturned;
}
(async () => {
console.log('Heads up! The next line makes an asynchronous call.');
console.log('Result: ' + await waitingAsync()); // 'Hello world!'
console.log('Done! This will be printed LAST! - Agreed?');
})();
console.log('done...?? This is LAST in the CODE. - I awaited \
"waitingAsync()" above. - So will this be printed at the very end??');
.as-console-wrapper { max-height: 100% !important; top: 0; }
The last asynchronous function is anonymous – without name – and gets
called immediately.
In fact, this is the only function that gets called directly in the snippet.
The function waitingAsync is only called indirectly (by the anonymous
function), and the function waiting is also called indirectly
(by waitingAsync).
A take away lesson
Don't ever put sequential/synchronous code after and outside a call to
an asynchronous function!
You will just confuse yourself if you do. – And even if you don't get
confused, other readers of your code almost certainly will be.