Using setTimeout with no timeout value while evaluating result of Promise.all - javascript

The Promise.all MDN docs contain an example of evaluating multiple Promise.all results, but within a setTimeout function without a timeout value.
From the docs:
// this will be counted as if the iterable passed is empty, so it gets fulfilled
var p = Promise.all([1,2,3]);
// this will be counted as if the iterable passed contains only the resolved promise with value "444", so it gets fulfilled
var p2 = Promise.all([1,2,3, Promise.resolve(444)]);
// this will be counted as if the iterable passed contains only the rejected promise with value "555", so it gets rejected
var p3 = Promise.all([1,2,3, Promise.reject(555)]);
// using setTimeout we can execute code after the stack is empty
setTimeout(function() {
console.log(p);
console.log(p2);
console.log(p3);
});
// logs
// Promise { <state>: "fulfilled", <value>: Array[3] }
// Promise { <state>: "fulfilled", <value>: Array[4] }
// Promise { <state>: "rejected", <reason>: 555 }
Can someone help explain what this achieves, with a few more words than the comment in the code?

setTimeout called with no delay value just puts the passed in function on the queue to be executed by the JavaScript event loop. That is usually the next tick, although there could be other things on the queue already scheduled, so the function will be after those.
Promise resolution gets scheduled similarly by putting it on the queue. That means setTimeout will schedule the function completion immediately after the promises are finalized. In turn, this means that the value of p, p2, and p3 will be pending in the current run of the JavaScript event loop, then in the final state when the function delayed by setTimeout is called.
Demonstrating the flow of the program:
//console.logs from StackOverflow snippets don't print the state of the promise
//you can open the browser developer tools and click the button to run the snippet
//in order to see that output
var p = Promise.all([1,2,3]); //<-- a pending promise is created
p.finally(() => console.log("promise fulfilled", p));
console.log("this call is executed immediately", p); //<-- the same run of the event loop
setTimeout(function() {
console.log("this call is scheduled to execute in the future", p); //<-- to be executed in a subsequent run of the loop.
});

In JavaScript promises have a higher priority than timeouts so in theory they should all be fulfilled and ready to be logged by the time the function in the timeout is executed.

Related

How JavaScript determines that a Promise has settled

In the following code how does JavaScript determine that the state of myPromise has become "fulfilled"? I.e., how is the determination made that it's time to put the .then() handler into the microqueue for subsequent execution?
const myPromise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('Resolved promise: ');
}, 2000);
});
myPromise.then((resolvedValue) => {
console.log(resolvedValue + 'The .then() handler is now running');
});
// Output (after ~2 seconds): "Resolved promise: The .then() handler is now running"
Answer
You calling the function resolve changes the state of the promise, thus JS can know that the promise's state has changed by you calling resolve.
The callback attached to that promise with .then() will then be known to be scheduled (as a microtask).
Explaining your code
You instantiate a new Promise and provide a callback that is run immediately. The callback schedules a task to run after 2000ms. That task will resolve the promise upon execution.
After having constructed the promise, you then attach a callback via .then() to the returned promise. This will only execute once the promise has fulfilled.
Now your synchronous code has run to completion, so the event loop has time to execute another task. The next task may be the one after 2000ms, which resolves the promise (and therefore sets its state to "fulfilled").
Once the task to resolve the promise has finished executing, a microtask will be scheduled to run immediately after: That microtask will run the .then() callback. This will finally log your string.

What happens exactly when we call .then before a Promise is resolved?

var promise1 = new Promise(resolve => setTimeout(resolve, 5000, 123));
setTimeout(_ => {
promise1.then(console.log)
}, 500);
The functions supplied will be queued up and fired once the promise resolves. It will also return a new promise which will be fulfilled or rejected with the return value of said functions.
If the promise is already settled, the functions will be fired on the next tick on the event loop.
In your example, the function supplied will still log it 5 seconds after the promise is made, which is 4.5 seconds after the second timeout is called.
then tells the promise what to do when it finishes. You should always expect to call it before it is resolved. However, given that the devs are smart it doesn't matter if the promise resolves before then can be called.

will the setImmediate or the IO callback run first?

in this diagram is says that the event Loop will run the I/O callbacks (the axios request), then the check phase (setImmediate), but when i tested this, it was the opposite, i need explanation of the execution of that code.
console.log('first'); // logs instantly
setImmediate(() => console.log('immediate')); // will be executed in the next iteration of the event loop.
new Promise((resolve) => { // starts in promise in pending state
console.log('insidePromise');
const data = 'Promise';
resolve(data);
}).then((d) => console.log(d)); // prints after Promise is resolved
Output -
first
insidePromise
Promise
immediate
When the Promise is called it starts in a pending state and waiting for resolve.
setImmediate - triggers here before resolve is processed as setImmediate will have to be executed in the event loop.
https://nodejs.dev/learn/understanding-javascript-promises
Once a promise has been called, it will start in a pending state. This means that the calling function continues executing, while the promise is pending until it resolves, giving the calling function whatever data was being requested.
https://nodejs.dev/learn/understanding-setimmediate
Any function passed as the setImmediate() argument is a callback that's executed in the next iteration of the event loop.

How and when Promise `.then`, `.catch` and `.finally` land in EventLoop micro-tasks queue?

Are .then, .catch and .finally land in JS Event Loop immediately after being registered (when appropriate code is interpreted by JS runtime) or somehow differently?
The less theoretical face of this question is the following :
I have the following code ( Link to JSBin (online JS execution environment) )
l = console.log
const g = (title) => {
return (a) => {
l(title + a)
return a+1;
}
};
const gA = g("A ")
const gB = g("B ")
const throwError = (title) => {
return (a) => {
l(title + a)
throw new Error("Error from promise " + title + a)
}
};
promise = new Promise((resolve, reject) => {
resolve(1)
})
l("Started application")
promise
.then(gA)
.then(gA)
.then(throwError("A "))
.catch(err => {
l(err.message);
})
promise
.then(throwError("B "))
.then(gB)
.then(gB)
.catch(err => {
l(err.message);
})
l("Finish main function")
that resolves to the following output in the console
"Started application"
"Finish main function"
"A 1"
"B 1"
"A 2"
"A 3"
"Error from promise A 3"
"Error from promise B 1"
If promise callbacks were to be executed in the order they were registered, I would expect "Error from promise B 1" to be higher in this output. But they appear in the end. Why is this happening?
Do .then, .catch and .finally land in JS Event Loop immediately after being registered or somehow differently?
Those promise methods themselves never land in the queue. The task queue contains promise reactions, which basically say "run callbacks X/Y on the result of promise Z". (And they're always pairs of then callbacks, as that's what catch and finally use internally).
These promise reactions are scheduled as soon as the promise is settled, or scheduled immediately during the then call when promise already was settled.
If promise callbacks were to be executed in the order they were registered
They are. But this holds only per promise. The only promise in your code that you are attaching multiple callbacks to is promise, and gA does run earlier than throwError("B "). There are no guarantees for callbacks that are attached to different promises, as those promises would settle at different times anyway.
From the code you shares, let's say promise A to below:
promise
.then(gA)
.then(gA)
.then(throwError("A "))
.catch(err => {
l(err.message);
})
and promise B to below:
promise
.then(throwError("B "))
.then(gB)
.then(gB)
.catch(err => {
l(err.message);
})
Then as we know asynchronous behavior of promises, console.log will always finish execution before hence it prints at very first but meanwhile promise A & B both have started already but will start printing after console.logs.
According to order i.e promise A first and promise B second, both falls on their first .then i.e. promise A -> .then(1st) is executed then promise b -> .then(1st) hence it prints
"A 1" //no exception called here hence will start execution of next .then
"B 1" //prints from throw called here and remains alive with 1 hence will never allow any of next .then() to execute.
"A 2" // 2nd .then executed
"A 3" // 3rd .then executed followed by throw and again remains alive with 3
If error occurs then will enters catch, and then "throw" will always finish execution in the end, so as per the order promise A is before promise B, below is shown
"Error from promise A 3" // 3 because of last successful increment
"Error from promise B 1" // 1 because of throw did not allow increment
So here is the order I believe, if .then() return cascading promises then it depends on time of a response resolve.
When you call resolve() a promise fullfillment / rejection gets queued into the promise microtask queue. Then when the task gets run, all respective .thens / .catchs attached to that promise get run in order of attachment.
promise.then(first);
promise.then(second);
Now .then returns itself a Promise, whose resolve gets called when the callback gets executed. Therefore a .then attached to a Promise returned by .then gets executed at least one microtask later:
const secondPromise = firstPromise.then(first);
// one microtick after first run, secondPromise fullfills, and therefore attached .thens get run
secondPromise.then(second);

Why are my promises not processing asynchronously?

I have two functions, one function, asyncTest_returnPromise simply returns a promise, and another function, asyncTest, calls that promise 3 times by calling Promise.all(promises) on an array of promises. Within each promise in the array, I send one value ("Foo") to the console before the asyncTest_returnPromise function is called, and send another value ("Bar") to the console after it is called.
Given my understanding of the Promise.all function, I would have expected each promise within the array to be processed only after the subsequent promise had been fulfilled, meaning that as both console.log statements are within each promise in the array, the result would have been:
Foo
Bar
Foo
Bar
Foo
Bar
Instead, however, the output seems to be:
Foo
Foo
Foo
Bar
Bar
Bar
As all instances of "Foo" have been sent to the console before even the first "Bar", it seems to me these promises must be being processed concurrently.
Here are the two functions:
function asyncTest_returnPromise() {
return new Promise((resolve, reject) => {
resolve()
})
}
function asyncTest() {
var promises = []
for (i = 0; i < 3; i++) {
promises.push(new Promise((resolve, reject) => {
console.log("Foo")
asyncTest_returnPromise().then(_ => {
console.log("Bar")
resolve();
})
}))
}
Promise.all(promises)
}
asyncTest();
So I would like to know, have I misunderstood the purpose of Promise.all or how it works? Has some other part of my code caused all "foo" outputs before all the "bar" outputs? Is there something I need to do to have the entirety of each promise complete before moving on to the next one?
They are running asynchronously.
Each time around the loop you:
Log Foo
Create a promise (which resolves immediately)
Use then to queue up a function to run after the promise resolves
Then the loop goes around for the next iteration.
Afterwards, when the asyncTest function is finished , the event loop is free to look at the queued up functions.
Since all the promises are resolved, they each run (and log Bar).
If you want to wait for a promise before going around the loop again, then you should look at the await keyword which would let asyncTest pause (and go to sleep) until a promise resolved.
It's just how Event Loop works. Let me explain how it's processed in simple words:
First, the loop starts and runs 3 times. Each time it creates a new microtask and puts it in the microtask queue for later handling. The order in which those microtasks have been created is kept.
Now those microtasks are being handled one at a time, synchronously. Each of those microtasks (which are run synchronously as the Loop runs) executes console.log('Foo'); and then creates another microtask (another Promise) which are queued by the .then() call. So this additional microtask is put into the queue (behind those three first microtasks). It happens for every microtask created by the loop (so it's 3 times). This is where you have Foo printed 3 times.
Now that all our microtasks (3) have been processed, the Event Loop keeps going and takes the next microtasks for processing. This time it's a microtask that runs console.log('Bar');. It happens also three times. Here's where you get Bar printed 3 times.
Of course, there's more that happens in the Event Loop but this is essentially what happens with your Promises here.

Categories

Resources