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
Related
When using Javascript promises, does the event loop get blocked?
My understanding is that using await & async, makes the stack stop until the operation has completed. Does it do this by blocking the stack or does it act similar to a callback and pass of the process to an API of sorts?
When using Javascript promises, does the event loop get blocked?
No. Promises are only an event notification system. They aren't an operation themselves. They simply respond to being resolved or rejected by calling the appropriate .then() or .catch() handlers and if chained to other promises, they can delay calling those handlers until the promises they are chained to also resolve/reject. As such a single promise doesn't block anything and certainly does not block the event loop.
My understanding is that using await & async, makes the stack stop
until the operation has completed. Does it do this by blocking the
stack or does it act similar to a callback and pass of the process to
an API of sorts?
await is simply syntactic sugar that replaces a .then() handler with a bit simpler syntax. But, under the covers the operation is the same. The code that comes after the await is basically put inside an invisible .then() handler and there is no blocking of the event loop, just like there is no blocking with a .then() handler.
Note to address one of the comments below:
Now, if you were to construct code that overwhelms the event loop with continually resolving promises (in some sort of infinite loop as proposed in some comments here), then the event loop will just over and over process those continually resolved promises from the microtask queue and will never get a chance to process macrotasks waiting in the event loop (other types of events). The event loop is still running and is still processing microtasks, but if you are stuffing new microtasks (resolved promises) into it continually, then it may never get to the macrotasks. There seems to be some debate about whether one would call this "blocking the event loop" or not. That's just a terminology question - what's more important is what is actually happening. In this example of an infinite loop continually resolving a new promise over and over, the event loop will continue processing those resolved promises and the other events in the event queue will not get processed because they never get to the front of the line to get their turn. This is more often referred to as "starvation" than it is "blocking", but the point is that macrotasks may not get serviced if you are continually and infinitely putting new microtasks in the queue.
This notion of an infinite loop continually resolving a new promise should be avoided in Javascript. It can starve other events from getting a chance to be serviced.
Do Javascript promises block the stack
No, not the stack. The current job will run until completion before the Promise's callback starts executing.
When using Javascript promises, does the event loop get blocked?
Yes it does.
Different environments have different event-loop processing models, so I'll be talking about the one in browsers, but even though nodejs's model is a bit simpler, they actually expose the same behavior.
In a browser, Promises' callbacks (PromiseReactionJob in ES terms), are actually executed in what is called a microtask.
A microtask is a special task that gets queued in the special microtask-queue.
This microtask-queue is visited various times during a single event-loop iteration in what is called a microtask-checkpoint, and every time the JS call stack is empty, for instance after the main task is done, after rendering events like resize are executed, after every animation-frame callback, etc.
These microtask-checkpoints are part of the event-loop, and will block it the time they run just like any other task.
What is more about these however is that a microtask scheduled from a microtask-checkpoint will get executed by that same microtask-checkpoint.
This means that the simple fact of using a Promise doesn't make your code let the event-loop breath, like a setTimeout() scheduled task could do, and even though the js stack has been emptied and the previous task has been executed entirely before the callback is called, you can still very well lock completely the event-loop, never allowing it to process any other task or even update the rendering:
const log = document.getElementById( "log" );
let now = performance.now();
let i = 0;
const promLoop = () => {
// only the final result will get painted
// because the event-loop can never reach the "update the rendering steps"
log.textContent = i++;
if( performance.now() - now < 5000 ) {
// this doesn't let the event-loop loop
return Promise.resolve().then( promLoop );
}
else { i = 0; }
};
const taskLoop = () => {
log.textContent = i++;
if( performance.now() - now < 5000 ) {
// this does let the event-loop loop
postTask( taskLoop );
}
else { i = 0; }
};
document.getElementById( "prom-btn" ).onclick = start( promLoop );
document.getElementById( "task-btn" ).onclick = start( taskLoop );
function start( fn ) {
return (evt) => {
i = 0;
now = performance.now();
fn();
};
}
// Posts a "macro-task".
// We could use setTimeout, but this method gets throttled
// to 4ms after 5 recursive calls.
// So instead we use either the incoming postTask API
// or the MesageChannel API which are not affected
// by this limitation
function postTask( task ) {
// Available in Chrome 86+ under the 'Experimental Web Platforms' flag
if( window.scheduler ) {
return scheduler.postTask( task, { priority: "user-blocking" } );
}
else {
const channel = postTask.channel ||= new MessageChannel();
channel.port1
.addEventListener( "message", () => task(), { once: true } );
channel.port2.postMessage( "" );
channel.port1.start();
}
}
<button id="prom-btn">use promises</button>
<button id="task-btn">use postTask</button>
<pre id="log"></pre>
So beware, using a Promise doesn't help at all with letting the event-loop actually loop.
Too often we see code using a batching pattern to not block the UI that fails completely its goal because it is assuming Promises will let the event-loop loop. For this, keep using setTimeout() as a mean to schedule a task, or use the postTask API if you are in a near future.
My understanding is that using await & async, makes the stack stop until the operation has completed.
Kind of... when awaiting a value it will add the remaining of the function execution to the callbacks attached to the awaited Promise (which can be a new Promise resolving the non-Promise value).
So the stack is indeed cleared at this time, but the event loop is not blocked at all here, on the contrary it's been freed to execute anything else until the Promise resolves.
This means that you can very well await for a never resolving promise and still let your browser live correctly.
async function fn() {
console.log( "will wait a bit" );
const prom = await new Promise( (res, rej) => {} );
console.log( "done waiting" );
}
fn();
onmousemove = () => console.log( "still alive" );
move your mouse to check if the page is locked
An await blocks only the current async function, the event loop continues to run normally. When the promise settles, the execution of the function body is resumed where it stopped.
Every async/await can be transformed in an equivalent .then(…)-callback program, and works just like that from the concurrency perspective. So while a promise is being awaited, other events may fire and arbitrary other code may run.
As other mentioned above... Promises are just like an event notification system and async/await is the same as then(). However, be very careful, You can "block" the event loop by executing a blocking operation. Take a look to the following code:
function blocking_operation_inside_promise(){
return new Promise ( (res, rej) => {
while( true ) console.log(' loop inside promise ')
res();
})
}
async function init(){
let await_forever = await blocking_operation_inside_promise()
}
init()
console.log('END')
The END log will never be printed. JS is single threaded and that thread is busy right now. You could say that whole thing is "blocked" by the blocking operation. In this particular case the event loop is not blocked per se, but it wont deliver events to your application because the main thread is busy.
JS/Node can be a very useful programming language, very efficient when using non-blocking operations (like network operations). But do not use it to execute very intense CPU algorithms. If you are at the browser consider to use Web Workers, if you are at the server side use Worker Threads, Child Processes or a Microservice Architecture.
Consider the following code:
import events from 'events';
const eventEmitter = new events.EventEmitter();
eventEmitter.on('flush', (arg)=>{
let i=0;
while(i<=100000){
console.log(i, arg);
i++;
}
})
setTimeout(()=>{
eventEmitter.emit('flush', `Fourth event`);
}, 5000);
setTimeout(()=>{
eventEmitter.emit('flush', `Third event`);
}, 4000);
setTimeout(()=>{
eventEmitter.emit('flush', `First event`);
}, 2000);
setTimeout(()=>{
eventEmitter.emit('flush', `Second event`);
}, 3000);
Output1:
1 First event
2 First event
3 First event
.
.
.
1 Second event
2 Second event
3 Second event
.
.
.
1 Third event
2 Third event
3 Third event
.
.
.
1 Fourth event
2 Fourth event
3 Fourth event
What I wanted to know was, does the event that is emitted get completed first then only the second event get emitted? Or can I expect something like this:
Output2:
1 First event
1 Second event
2 Third event
3 Fourth event
3 Third event
.
.
.
What if I wrote the emitter.on function like this:
eventEmitter.on('flush', (arg)=>{
const mFunc = async ()=>{
let i=0;
while(i<=100000){
console.log(i, arg);
i++;
}
}
mFunc();
})
I was expecting output like Output2, but, instead I got something similar to Output1. Could someone please explain me this behaviour?
Also, consider this case:
eventEmitter.on('flush', (arg)=>{
const mFunc = async ()=>{
let i=0;
while(i<=100000){
console.log(i, arg);
i++;
await updateValueInDatabase(i);
}
}
mFunc();
})
Now, what would be the behaviour of the function?
Coalescing my comments into an answer and adding more commentary and explanation...
Emitting an event from an EventEmitter is 100% synchronous code. It does not go through the event loop. So, once you call .emit(), those event handlers will run until they are done before any other system events (e.g. your timers) can run.
Calling an async function is NO different from the calling point of view at all than calling a non-async function. It doesn't change anything. Now, inside an async function, you could use await and that would cause it to suspend execution and immediately return a promise, but without await, making a function async just makes it return a promise, but doesn't change anything else. So, calling the async version of mFunc() is exactly the same as if it wasn't async (except it returns a promise which you don't use). Doesn't changing sequencing of events at all.
Since you apparently thought it would change things, I'd really suggest reading a bunch more about what an async function is because your perception of what it does is apparently different than what it actually does. All async functions return a promise and catch internal exceptions which they turn into a rejected promise. If you don't use await, they just run synchronously like everything else and then return a promise.
Calling an async function returns ONLY when you hit an await or when the function finishes executing and hits a return or the implicit return at the end of the function. If there's no await, it just runs like a normal function and then returns a promise (which you aren't doing anything with). As I said above, you really need to read more about what async functions actually are. Your current understanding is apparently incorrect.
Here are some summary characteristics of an async function:
They always return a promise.
That promise is resolved with whatever value the function eventually returns with a return statement or resolved with undefined if no return statement.
They run synchronously until they either hit a return statement or an await.
At the point they hit an await, they immediately return a promise and further execution of the function is suspended until the promise that await is on is resolved.
When they suspend and return a promise, the caller receives that promise and keeps executing. The caller is not suspended unless the caller also does an await on that promise.
They also catch any synchronous exceptions or other exceptions that aren't themselves in an asynchronous callback and turn those exceptions into a rejected promise so the promise that the async function returns will be rejected with the exception as the reason.
So, if there is no await in an async function, they just run synchronously and return a promise.
In your last version of code, if you end up making your event emitter handler actually be asynchronous and actually await long enough for other things to get some cycles, then you can create some competition between timers and promises waiting to notify their listeners. The promises waiting to notify their listeners will get to run before the timers. That makes your situation of mixing those two types very complicated and very dependent upon timings. A non-ending sequence of promises waiting to notify their listeners can make timers wait until there are no more promises waiting to notify. But, if there's a moment with no promises waiting to notify, then your next timer will fire and will kick off it's own set of promise-driven operations and then all the promise-driven operations will likely interleave.
Also, emitter.emit() is not promise-aware. It doesn't pay any attention to the return value from the callbacks that are listening for the emit. So, it doesn't make any difference at all to emitter.emit() whether the listeners are async or not. As soon as they return (whether they returned a promise or not), it just goes right on to whatever it was going to do next. Promises only influence code flow if the recipient uses them with await or .then() and .catch(). In this case, the recipient does nothing with the return value so emitter.emit() just goes right onto its next order of business and executes that.
okay, so, if I have a bunch of async function arrays [async1, async2, async3, ....], and they all have await statements internally, what would be the best way to execute them in sequential order? i.e. one after other in order of their index?
Well, if you had an array of async functions that properly resolve their promise when they are actually done with their work, you can execute them sequentially by just looping through the array with an await.
async function someFunc() {
const jobs = [asyncFn1, asyncFn2, asyncFn3];
for (let job of jobs) {
let result = await job(...);
}
}
Internally, the EvenEmitter hold an array of listeners for each event type. When you emit an event type, the EvenEmitter just goes through it's array of listeners and execute it. Therefore, the listeners are executed in the order it was added by addListener or on method.
To answer your questions there are two parts: a) it executes in the order you added the listener and b) it depends if the listeners are async or not. If your listeners are synchronous, then you can expect that behavior. If your listeners are async, you can't expect that. Even if your execute your async synchronously, they will not necessarily resolve at the same time because it's the nature of being async.
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
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);
I have this code for managing dashboard which contains approximate 100 of independent checks.
Check results are received via AJAX call.
There is one initial request for each check at start. After result is received for particular check, the code recursively waits for set timeout and repeats the request again for that check again.
One promise = one check.
I am wondering why promises starts to resolve only after each one of them is pending (none of them is in timeout period). And that is even if response from server is "instantaneous", they just wait for the last promise in cycle.
const TIMEOUT = 4000;
function checkForUpdate(environment, application, check) {
Dashboard.setCheckPending(environment, application, check);
return Communicator.getStatus(environment, application, check)
.then(status => {
Dashboard.updateCheckCell(environment, application, check, status);
Dashboard.updateEnvironmentCell(environment, application);
setTimeout(() => {
return checkForUpdate(environment, application, check)
},
TIMEOUT
);
});
}
Communicator.getEnvMatrix()
.then(data => {
Dashboard.create(data);
$.each(data, (environment, applications) => {
$.each(applications, (application, checks) => {
$.each(checks, (key, check) => {
checkForUpdate(environment, application, check);
});
});
});
});
The question is also how to rewrite that so each of the checks waits just for its own result to be delivered and for set timeout.
EDIT (clarification):
Each of the 100 checks are independent, that is why I want to run AJAX for each of them as soon as I can (inside the $.each() loops).
The check is dependent only on itself. I don't want it to wait on any other check.
After the result of a check is received it has to wait for set timeout before it tries to retrieve its status again. That is why I encapsulated the recursive function within the setTimeout().
Even if I rewrite (see below) the setTimeout() as promise, the behavior stays the same unfortunately.
function delay(timeout) {
return new Promise(resolve => {
setTimeout(resolve, timeout);
});
}
function checkForUpdate(environment, application, check) {
Dashboard.setCheckPending(environment, application, check);
let promise = Communicator.getStatus(environment, application, check).promise();
return promise
.then(status => {
Dashboard.updateCheckCell(environment, application, check, status);
Dashboard.updateEnvironmentCell(environment, application);
return delay(TIMEOUT).then(() => {
return checkForUpdate(environment, application, check);
});
});
}
Your code runs $.each() synchronously. That means it is going to call every checkForUpdate() before anything else can run. Since standards-conforming promises are always resolved asynchronously (on some future tick), that means that every single request here will get started before ANY promise can run its .then() handler. That's how promises work. Only once the $.each() loop is done can the Javascript interpreter start to process the .then() handlers of resolved promises.
Also, it is unclear why you are trying to do a return checkForUpdate(environment, application, check) inside the setTimeout(). The return there does nothing. It's just returning to the setTimeout() callback which does nothing. The parent function has long since already returned so this is not chaining the next checkForUpdate() to the previous promise chain. If you wanted to chain them together, then you need to make a delay with a promise and return that promise like is shown in these references:
using setTimeout on promise chain
Delays between promises in promise chain
Delay chained promise
The unexpected thing is that even if all 100 requests are sent immediately after page loads, they are being resolved just few at a time and (roughly) in the order they've been sent. Roughly means 3, 2, 5, 1, 8, .... But I'd expect something like 3, 89, 12, 76, 21, 94, .... There seems to be some limit on how many promises can be run concurrently and in what order.
Another thing that will influence your ajax calls is that each browser has a limit on how many concurrent ajax calls it will allow to the same host. If you exceed that limit, it will queue them and not run subsequent ones until some earlier ones finish. Each browser sets its own limit and they've changed over time so I don't know exactly what the current limits are, but they are lowish. I know Chrome used to be something like 6 at at time to the same host. So, that will also affect the exact order that things complete.
When you hit this limit, Ajax calls will be sent in the order they were called by your code. So, if the limit was 6 per host, then your first 6 would be sent and the 7th request would only be sent when one of the first 6 finished and so on. That still doesn't guarantee a finish order, but it does affect the ability for a later request to finish before an earlier request.