Confusion with how JS engine runs promises? - javascript

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.

Related

How to use await with async function without creating a Promise?

I know an async function returns a Promise, so I thought of replacing this code:
const hi = function (delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('hi');
resolve();
},delay)
});
};
const bye = async () => {
await hi(1000);
await hi(1000);
await hi(1000);
await hi(1000);
return 'bye';
};
bye().then((msg) => {
console.log(msg);
});
with this code:
const hi = async (delay) => {
setTimeout(() => {
console.log('hi');
},delay);
};
const bye = async () => {
await hi(1000);
await hi(1000);
await hi(1000);
await hi(1000);
return 'bye';
};
bye().then((msg) => {
console.log(msg);
});
The only difference is I'm using an async function instead of a function which returns a Promise, since an async function return a promise. But the second code doesn't work as I expected it too. It just console.logs bye immediately and then after 1s console.logs 'hi' 4 times. Can you please help me understand the reason behind this?
One major difference in both code examples is related to the return value of hi function.
In the first code example, function hi returns a promise whereas in the second code example, function hi does not explicitly returns any value; as a result, the promise returned by the async function is implicitly resolved with the value of undefined without waiting for the setTimeout's callback function to execute.
A promise object doesn't makes anything asynchronous; its just a wrapper around an already asynchronous operation and the promise object notifies whether the asynchronous operation completed successfully or whether it failed.
In the first code example, you have a wrapper promise around the setTimeout. That wrapper promise is not fulfilled until resolve is called from within the callback function of the setTimeout.
In the second code example, the promise returned by the async function isn't automatically bound to the asynchronous code inside the function.
After calling setTimeout, code execution reaches the end of the hi function; As there is no explicit return statement, promise returned by the async function is fulfilled with the value of undefined. This happens before the callback function of the setTimeout is called.
What's confusing you here is the setTimeout, your await hi( will actually await, but there isn't anything to wait for, that promise will almost immediately resolve and return so all 4 of them will execute almost instantaneously, then log "bye", then after 1 second your timeout will trigger and console.log "hi".
This answer adds some additional info to the one provided by Yousaf.
To shortly answer your comment:
Thanks for the explanation. Is there any way to write the hi function without using the 'Promise' keyword?
The answer is no. To convert a function using a callback interface to one that uses a promise you will have to manually create a promise somewhere. Functions using an older interface (usually) don't return promises, therefore you cannot await them. You can only await objects that are thenable (i.e. has a "then" method). If you await a non-thenable value or object, the await statement is essentially ignored.
MDN recommends wrapping callback methods at the lowest level possible:
Creating a Promise around an old callback API
A Promise can be created from scratch using its constructor.
This should be needed only to wrap old APIs.
In an ideal world, all asynchronous functions would already return
promises. Unfortunately, some APIs still expect success and/or failure
callbacks to be passed in the old way. The most obvious example is the
setTimeout() function:
setTimeout(() => saySomething("10 seconds passed"), 10*1000);
Mixing old-style callbacks and promises is problematic. If
saySomething() fails or contains a programming error, nothing
catches it. setTimeout is to blame for this.
Luckily we can wrap setTimeout in a promise. Best practice is to
wrap problematic functions at the lowest possible level, and then
never call them directly again:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback);
Basically, the promise constructor takes an executor function that
lets us resolve or reject a promise manually. Since setTimeout()
doesn't really fail, we left out reject in this case.
If you would apply this info to your situation, this means wrapping the setTimeout() call within a Promise constructor like shown above.
// The only responsibility of this function is converting the old
// callback interface, to a promise interface.
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Then use this new function in hi() instead of setTimeout().
async function hi(delay) {
await wait(delay);
console.log('hi');
}
A promise captures the result of an asynchronous processing that just started.
On the version of the code without promises, the asynchronous processing happens the same way as on the first version but there is nobody there waiting for it when it completes.
The second version of the hi() function is synchronous. It completes immediately. The async keyword does not have any effect on it. Also await hi() does not help, the function is still synchronous and it completes immediately.

async function javascript is not running in background?

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.

Async and await in javascript [duplicate]

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();

Async/Await confusion in JS

I was learning asynchrony in JS and came across Async/Await. Then, I came across this code:
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
msg(); // Message: 🤡 <-- after 2 seconds
Thus, I have questions on the above code. Firstly, how can async function msg() return message value if the function itself returns only undefined Promise, that is, the function does not explicitly use return keyword. Secondly, does await returns Promise or value itself having been unwrapped from Promise?
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
The above function is a function which returns a promise when it is called, but will only be resolved after 2 seconds.
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
the above function is asynchronous function which awaits for the promise to be resolved (in your case, after 2 seconds) and then only the console.log() triggers.
Note: any lines of code below the function msg() gets executed irrespective of the promise and the asynchronous function.
Running code snippet
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
console.log(scaryClown()); // try without await, so it will not wait for the result and prints first, but an unresolved promise
const msg = await scaryClown(); // actual result which is resolved, because we awaited
console.log('Message:', msg); // Prints once the resolved value is available.
}
msg(); //executes first but only prints the value once it is resolved.
console.log('I will continue executing irrespective of async func msg()');
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
await resolves promise and return resolved value. so above code with then can be written like below. hope this makes you understand async await.
function msg() {
scaryClown().then(msg => {
console.log('Message:', msg);
});
}
This is what happens:
When the async function (i.e. msg) is invoked, it creates a promise object (let's call it P), which it will return when it deals with an await expression. scaryClown() executes, which sets the timer and returns yet another promise object; let's call it Q. Then await is processed: at that moment the function returns the promise P. This promise is not resolved at that moment; it is pending.
If then there is any other code that followed the call of msg(), then that is executed like would be the case after any function call. Execution continues until the call stack is empty.
Then, some time later the timer event that was scheduled by scaryClown() will fire, and the corresponding promise Q resolves. Because this promise Q was in an await expression, this resolution triggers a restoration of the execution context of msg(), where the await expression is now evaluated to be Q's resolved value, i.e. "🤡".
The variable msg gets this value assigned, and it is displayed with console.log. Then the function completes, in this particular case without any explicit return. Remember that the function had already returned, so this is not really a normal return: code execution really ends here, as there is nothing on the call stack. This makes the promise P resolve. If there had been an explicit return statement, with a value, the promise P would have been resolved with that value. But since there is no such return statement in your example, promise P resolves with undefined.
As you don't do anything with the value returned by msg(), you don't really see what it returns. If you would have done:
msg().then(console.log);
... you would have seen undefined in the console.
Your questions:
how can async function msg() return message value if the function itself returns only undefined Promise, that is, the function does not explicitly use return keyword.
The msg() call does currently not provide you with the message value. Instead it uses console.log to output it, but that is not the value returned by msg(). The returned value is a promise. Even if it had an explicit return keyword, it would still not make msg() return something else than a promise. The only difference would be what value that promise would eventually resolve to.
does await returns Promise or value itself having been unwrapped from Promise?
await does two things at two different moments in time. First it makes the function return to the caller. Then, when the promise that it awaits resolves, it makes the function continue to run again, and await represents at that moment the promised value, which can therefore be assigned to a variable (or used in another way).
I personally don't use the verb "unwrapped" when talking about promises. But yes, the expression await promise evaluates (asynchronously) to the value that promise fulfils to.

Javascript Await/Async Feature - What if you do not have the await word in the function?

I am learning about Javascript ES2017 async/await feature.
Been reading a lot about it, and came across an understanding that await is like yield, and allows us to wait for the promises to complete.
From https://javascript.info/async-await
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();
The question I have is, can I add await to every single line of code?
Or what happens if I remove the word await?
And another question is, if async/ await makes the code seems to run synchronously and in order, why don't we don't use it at all (means make everything stay synchronous in the first place?)
Thank you!
async functions are just syntactic sugar around Promises. It does nothing to change the function to be synchronous. In fact any function that is async implicitly returns a Promise object.
All await does is provide a convenient way to wait for a Promise. If you remove the await keyword, then the Promise will not be "unwrapped," which is not what you want if your function is going to operate on the result of that Promise.
To illustrate, this is the desugared version of your async function:
function showAvatar() {
let githubUser;
// read our JSON
return fetch('/article/promise-chaining/user.json').then(response => {
return response.json();
}).then(user => {
// read github user
return fetch(`https://api.github.com/users/${user.name}`);
}).then(githubResponse => {
return githubResponse.json();
}).then(user => {
githubUser = user;
// 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
return new Promise((resolve, reject) => setTimeout(resolve, 3000));
}).then(() => {
img.remove();
return githubUser;
});
}
So await essentially just adds a .then callback to a Promise. Without await being specified, you'll just have a Promise object, not the result of the Promise.
await tells the runtime to wait for the promise returned from the expression on the right-hand side to be fulfilled.
In the following code control is stopped in the async function until the promise returned by the fetch invocation is fulfilled, and the response value sent to the fulfilled promise is printed to the console.
async function go() {
let response = await fetch('http://www.example.com');
console.log(response); // print the response after some time
}
go();
If the await keyword was omitted then control would immediately continue to the next line of the function and the the promise returned by fetch would be immediately printed to the console.
async function go() {
let response = fetch('http://www.example.com');
console.log(response); // print the promise from fetch immediately
}
go();
await is a contextual keyword that only means something special when used inside a function marked async. By marking a function async you are telling the runtime to:
treat the function as a generator function
yield a value where the user types await (usually a promise)
wrap the function in special handling logic so that progress through the function only occurs when each promise yielded by await is fulfilled
In this way, asynchronous control flows can be written in a style closer to the traditional synchronous style. ie. without nesting, callbacks or visible promises. try..catch can also be used in the normal way.
As another answer mentions, async, and await are syntactic sugar that ask the runtime to use existing objects (generator functions and Promises) behind the scenes to make async code easier to read and write.
can you await every line
I would guess you can. If the expression on the right of the await results in a promise, then the async/await behaviour detailed above occurs.
If the expression on the right of the await does not result in a promise, then I would guess the value is wrapped in a resolved promise for you, and logic continues per the above, as though the value came from an immediately resolved promise. This is a guess.
First, async / await, work with promises. If you are not calling from an async function or the await sentence is not then compatible, it won't work. In most cases if you remove await, you are going to end with a Promise and not the value of the promise.
Secondly, there are some cases that you may want to continue with the execution of the code while you are resolving some asynchronous task.

Categories

Resources