Resolving of promise is done asynchronously even for Promise.resolve()? - javascript

console.log(1);
let p = Promise.resolve(2);
p.then(val => console.log(val));
console.log(3);
The above code prints 1 3 2, but since the promise resolving is done synchronously, shouldn't the callback also execute synchronously? Can anyone explain this behavior?

As per the mozilla developer networks docs:
[Promise] callbacks will never be called before the completion of the current run of the JavaScript event loop.
So your .then() callback is called after the current code has finished.

Javascript is single threaded (mostly). To allow JS to scale it relies heavily on asynchronous execution of code. So imagine making a phone call, you ask a question, the person on the other end of the line has to look up the answer, so you wait. All the time your waiting you're not doing anything. This is wasteful of your time.
Now imagine doing the same thing but this time you tell the person on the other end of the line to tell you when they've got the answer. You hang up and do something else. When the other person has finished they call you back to give you the answer to your question. This is a much more efficient use of your time.
In your code the Promise is you making the phone call. The then is the person you've called calling you back when they have the result and your (the person making the call that is) the Js thread.
So you do:
//1
console.log(1);
You then call someone (promise) and ask for them to do some work:
let p = Promise.resolve(2); //nothing printed here
They say they'll call you back:
.then(...) //nothing printed here
You do other work
//3
console.log(3);
They call you back:
//2
val => console.log(val)
1
3
2
but since the promise resolving is done synchronously
No it's not promises are resolved asynchronously
#daphtdazz makes a very good point on why it doesn't just print 2 straight away
console.log is immediate execution. So imagine the person you've just hung up on tries to call you back straight away. But you don't answer yet because you just need to finish this other task. Then you answer.

The code which is executed in .then callbacks of promise is asynchronous always.
The code inside the promise is not asynchronous.
console.log(1);
let p = new Promise((res, rej) => {
console.log('sync');
res(2);
});
p.then(val => console.log('async ' +val));
console.log(3);

Related

Can I use setTimeout() on JavaScript as a parallel processed function

I know JS is single threaded. But I have a function which takes time for the calculation. I would like it to work paralleled, so this function would not freeze next statement. Calculation inside of function will take approximately 1-2 seconds.
I used to create it using promise, but it still freeze the next statement.
console.log("start");
new Promise((res, rej) => {
/* calculations */
}).then((res) => console.log(res));
console.log("end");
Then I used setTimeout function with time interval 0. LoL
console.log("start");
setTimeout(() => {
/* calculations */
console.log(res);
}, 0);
console.log("end");
Outputs:
start
end
"calculation result"
Both cases shows similar result, but using promise prevented to show console.log("end") before calculation finishes. Using setTimeout works as I wanted, and shows console.log("end") before calculation, so it was not freeze till calculation done.
I hope it was clear enough. Now for me using setTimeout is the best solution, but I would be happy to hear your ideas or any other method calculating concurrently without setTimeout.
The code you write under new Promise(() => {..code here..}) is not asynchronous. The is a very common misconception that everything under the Promise block would run asynchronously.
Instead, this JS API just let's get us a hook of some deferred task to be done once the promise is resolved. MDN
Promises are a comparatively new feature of the JavaScript language that allow you to defer further actions until after a previous action
has completed, or respond to its failure. This is useful for setting
up a sequence of async operations to work correctly.
new Promise(() => {
// whatever I write here is synchromous
// like console.log, function call, setTimeout()/fetch()/async web apis
// if there are some async tasks like fetch, setTimeout.
// they can be async by themselves but their invocation is still sync
//
})
setTimeout is not the correct option either. Code under setTimeout would run when the event stack is empty and once it enters, it would block the main thread again.
The right approach to this would be to use Web Workers.

How the code after then runs if the Promise haven't been settled yet?

When we chain a Promise's using chain, and the Promise is returned by then, so how code that isn't part of the then chain runs before the all chain was finished? How does the code jump over then calls? Shouldn't all then calls performed before running the "console.log" line of code?
Here is the code:
Promise.resolve('333')
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {setTimeout(function(){console.log('Timeout');resolve(1);},10000);console.log('Something');})})
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {console.log('Something');resolve(2);})})
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {console.log('Something');resolve(3);})})
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {console.log('Something');resolve(4);})})
console.log("When?")
In the output "When?" is being printed first.. Now i can't understand how is it possible since the callback in the first then will run only in about 10 seconds and only after that, the callback will return a Promise that will run the second then?
Maybe i don't understand how the callback of then "goes with" the then function.
I thought that then implementation looks a little bit like:
then(callback){
let value= callback();
if(value is promise){
return value;
}
return Promise.resolve(value)
}
Good question. The answer is the Event Loop, which evaluates JavaScript. In short, there are microtasks and macrotasks. The console logs are macrotasks and the promises are microtasks. The Event Loop processes macrotasks first, then microtasks.
For a much more in depth explaination see: https://medium.com/javascript-in-plain-english/javascript-event-loop-y-promises-951ba6845899
There is a section with pretty much the same code in the question. Search for console.log('start'); to find it.
Seems like you are new to Javascript. I was too and this messed with me a lot. console.log will always print first since it's is not inside the "then". Js will run the log asynchronously, which means it will not wait for the promise to finish. To solve this or better, to make sure this does not happen, use the log inside the .then or use Promise.all(promiseName).then(log here).
Hope I was able to be of help!
This might help as well https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
PS: My first answer, please let me know if I did something wrong :p
It’s because code inside a promise is asynchronous and asynchronous code is always executed after all the synchronous code has been executed.
In your example you have 2 sync instructions, the first row where you are setting up the promise chain and the console.log. So first JavaScript is setting up the promises and then logging When. Only after this it is going to resolve the promises one by one.
If you want to make your code behave how you are expecting you can wrap it into an async function and then use the await keyword to stop the execution of the syncronous code untile the promisses are resolved:
//async is actually creating a promise under the hood for you
async function imAsyncFunction () {
// await will make sure that before proceeding to the next
// synchronous instruction (the console.log) the promise chain is resolved
await Promise.resolve('333')
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {setTimeout(function(){console.log('Timeout');resolve(1);},10000);console.log('Something');})})
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {console.log('Something');resolve(2);})})
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {console.log('Something');resolve(3);})})
.then((res)=>{console.log(res);return new Promise((resolve,reject)=> {console.log('Something');resolve(4);})});
console.log("When?");
}
imAsyncFunction();
// the log produced will be:
// 333
// Something
// Timeout
// 1
// Something
// 2
// Something
// 3
// Something
// When?
more info on async / await

What's the best way to run a promise synchronously when it's already resolved?

When a promise is resolved, it resumes code execution after any awaits in the next task.
So for instance:
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
}
foo();
console.log("end");
prints "start", "end", "foo". Even though the promise is already resolved, it still waits for any other code to execute first, and only after that it resumes and prints foo.
I would like to execute any awaits synchronously for resolved promises.
The only way I can think of to achieve this is to check if a promise has been fulfilled and wrap the await in an if statement so that it only waits if the promise is still pending. But figuring out if a promise is pending (and getting the resolved value) seems very tedious to do synchronously. Does anyone know if there's a better way?
More context
I have a game loop that needs to not run for more than a few milliseconds per frame. There is one specific function that I need to call in order to prepare some gltf assets. This function takes more than a second. So I'd like to devide this up into chunks, so it excecutes only part of this function every frame.
The easiest way to achieve this that I could think of was to make the function async and checking if a certain amount of time has passed. If more than a few milliseconds have passed, I will await waitForFrameRender(), which will essentially stop the execution until the next frame. However, this will add tons of awaits in the function (it has a bunch of loops), and most of them won't really have to wait for anything because the function hasn't been running for more than X amount of milliseconds yet.
Therefore it seemed to make more sense to skip these waits and run these parts synchronously.
In my current setup I have an if statement to check how much time has passed, and only await if it has actually been running for more than a few milliseconds. Which works to a certain degree, but the function that takes a second to execute also has nested functions that are async for the same reason. So the nested function calls still need an await.
I realize that this sounds like web workers are the solution. I haven't actually tried this yet but I feel like transferring the javascript object that the function returns will have too much overhead for this. And since it is ok for this function to not return immediately (even 20 seconds or so would be fine) simply making the function async seemed like the easiest way to do it.
If this is what you want move end into the promise. The other alternative would be to put end in its own promise
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
console.log("end");
}
foo();
or
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
}
foo();
await Promise.resolve();
console.log("end");
This is probably what you want. You need to await until foo() is done running.
async function run() {
console.log("start");
await foo();
console.log("end");
}
run();

Working of call stack when async/await is used

How does the Call Stack behave when async/await functions are used ?
function resolveAfter2Seconds() { // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
const asyncFuntion=async()=>{
const result = await resolveAfter2Seconds();
console.info("asyncFuntion finish, result is: ", result);
}
const first = async()=>{
await asyncFuntion();
console.log('first completed');
debugger;
}
const second = ()=>{
console.log('second completed');
debugger;
}
function main(){
first();
second();
}
main();
In the above code, when the first breakpoint is encountered in second(), I could see that the call stack contained main() and second(). And during the second breakpoint in first(), the call stack contained main() and first().
What happened to first() during the first breakpoint. Where is it pushed ? Assuming that the asyncFunction() takes some time to complete.
Someone please help.
First off, when you get to the breakpoint you hit in second, first has already executed and is no longer on the stack.
When we go into first, we instantly hit an await asyncFunction(). This tells JavaScript to not block on the result of the call, but feel free to go looking for something else to do while we're waiting. What does Javascript do?
Well, first of all, it does call asyncFunction(). This returns a promise, and will have started some asynchronous process that will later resolve it. Now we can't continue with the next line of first (ie, the console.log('first completed')) because our await means we can't carry on until the promise was fulfilled, so we need to suspend execution here and go find something else to do with our free time.
So, we look up the stack. We're still in the first() call from main, and now we can just return a promise from that call, which we will resolve when the asynchronous execution completes. main ignores that promise return value, so we continue right with second(). Once we've executed second, we look back to whatever called that, and carry on with synchronous execution in the way everyone would expect.
Then, at some point in the future, our promise fulfills. Maybe it was waiting on an API call to return. Maybe it was waiting on the database to reply back to it. In our example, it was waiting for a 2s timeout. Whatever it was waiting for, it now is ready to be dealt with, and we can un-suspend the first call and continue executing there. It's not "called" from main - internally the function is picked back up, just like a callback, but crucially is called with a completely new stack, which will be destroyed once we're done calling the remainder of the function.
Given that we're in a new stack, and have long since left the 'main' stack frame, how do main and first end up on the stack again when we hit the breakpoint inside it?
For a long time, if you ran your code inside debuggers, the simple answer was that they wouldn't. You'd just get the function you were in, and the debugger would tell you it had been called from "asynchronous code", or something similar.
However, nowadays some debuggers can follow awaited code back to the promise that it is resolving (remember, await and async are mostly just syntactic sugar on top of promises). In other words, when your awaited code finishes, and the "promise" under the hood resolves, your debugger helpfully figures out what the stack "should" look like. What it shows doesn't actually bear much resemblance to how the engine ended up calling the function - after all, it was called out of the event loop. However, I think it's a helpful addition, enabling us all to keep the mental model of our code much simpler than what's actually going on!
Some further reading on how this works, which covers much more detail than I can here:
Zero-cost async stack traces
Asynchronous stack traces

Promises not resolving in the way I would expect

I have the following:
for (let job of jobs) {
resets.push(
new Promise((resolve, reject) => {
let oldRef = job.ref
this._sequenceService.attachRef(job).then(() => {
this._dbService.saveDoc('job', job).then(jobRes => {
console.log('[%s / %s]: %s', oldRef, jobRes['jobs'][0].ref, this._moment.unix(job.created).format('DD/MM/YYYY HH:mm'))
resolve()
}).catch(error => {
reject(error)
})
}).catch(error => {
reject(error)
})
})
)
}
return Promise.all(resets).then(() => {
console.log('Done')
})
this._sequenceService.attachRef has a console.log() call.
When this runs, I am seeing all the console logs from the this._sequenceService.attachRef() call and then I see all the logs in the saveDoc.then() call. I was expecting to see them alternate. I understand that according to this article, promises don't resolve in order but I wouldn't expect my promise to resolve until I call resolve() so would still expect alternating logs, even if not in order.
Why is this not the case?
Your code can be written a lot cleaner by avoiding the promise anti-pattern of wrapping a promise in a new manually created promise. Instead, you just push the outer promise into your array and chain the inner promises to the outer one by returning them from inside the .then() handler. It can all be done as simply as this:
for (let job of jobs) {
let oldRef = job.ref;
resets.push(this._sequenceService.attachRef(job).then(() => {
// chain this promise to parent promise by returning it
// inside the .then() handler
return this._dbService.saveDoc('job', job).then(jobRes => {
console.log('[%s / %s]: %s', oldRef, jobRes['jobs'][0].ref, this._moment.unix(job.created).format('DD/MM/YYYY HH:mm'));
});
}));
}
return Promise.all(resets).then(() => {
console.log('Done')
}).catch(err => {
console.log(err);
});
Rejections will automatically propagate upwards so you don't need any .catch() handlers inside the loop.
As for sequencing, here's what happens:
The for loop is synchronous. It just immediately runs to completion.
All the calls to .attachRef() are asynchronous. That means that calling them just initiates the operation and then they return and the rest of your code continues running. This is also called non-blocking.
All .then() handlers are asynchronous. The earliest they can run is on the next tick.
As such this explains why the first thing that happens is all calls to .attachRef() execute since that's what the loop does. It immediately just calls all the .attachRef() methods. Since they just start their operation and then immediately return, the for loop finishes it's work pretty quickly of launching all the .attachRef() operations.
Then, as each .attachRef() finishes, it will trigger the corresponding .saveDoc() to be called.
The relative timing between when .saveDoc() calls finish is just a race depending upon when they got started (how long their .attachRef() that came before them took) and how long their own .saveDoc() call took to execute. This relative timing of all these is likely not entirely predictable, particularly if there's a multi-threaded database behind the scenes that can process multiple requests at the same time.
The fact that the relative timing is not predictable should not be a surprise. You're running multiple two-stage async operations purposely in parallel which means you don't care what order they run or complete in. They are all racing each other. If they all took exactly the same time to execute with no variation, then they'd probably finish in the same order they were started, but any slight variation in execution time could certainly change the completion order. If the underlying DB gets has lock contention between all the different requests in flight at the same time, that can drastically change the timing too.
So, this code is designed to do things in parallel and notify you when they are all done. Somewhat by definition, that means that you aren't concerned about controlling the precise order things run or complete in, only when they are all done.
Clarifying my comments above:
In this case the promises are at four levels.
Level 0: Promise created with Promise.all
Level 1: Promise created with new Promise for each Job
Level 2: Promise as generated by this._sequenceService.attachRef(job)
Level 3: Promise as generated by this._dbService.saveDoc('job', job)
Let's say we have two jobs J1 and J2
One possible order of execution:
L0 invoked
J1-L1 invoked
J2-L1 invoked
J1-L2 invoked
J2-L2 invoked
J1-L2 resolves, log seen at L2 for J1
J1-L3 invoked
J2-L2 resolves, log seen at L2 for J2
J2-L3 invoked
J1-L3 resolves, log seen at L3 for J1
J1-L1 resolves
J2-L3 resolves, log seen at L3 for J2
J2-L1 resolves
L0 resolves, 'Done' is logged
Which is probably why you see all L2 logs, then all L3 logs and then finally the Promise.all log

Categories

Resources