How to understand this Promise execution order? - javascript

I don’t understand why this piece of code results in such an order? Could anyone elaborate on this? I thought Promises were like a FIFO queue, but the nested Promise functions seems a little bit unpredictable, or maybe using some other data structure?
new Promise(resolve => {
resolve()
})
.then(() => {
new Promise(resolve => {
resolve()
})
.then(() => {
console.log(1)
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3.1)
})
})
.then(() => {
console.log(1.1)
new Promise((resolve => {
resolve()
}))
.then(() => {
new Promise(resolve => {
resolve()
})
.then(() => {
console.log(4)
})
.then(() => {
console.log(6)
})
}).then(() => {
console.log(5)
})
}).then(() => {
console.log(3)
})
console.log(0)
Output:
0
1
1.1
2
3
3.1
4
5
6

Promises are async. This means everytime you create a new promise- a new async operation starts.
What is async operation in JS? First you need to understand that JS operates on a single thread no matter what you do. So, to make it looks like its asynchronous- there is something called the "event loop" (took the link from comment to original post, tnx #Taki for the great source).
In general, the event loop stores all the async functions and "slips" in the actions between the main code actions. This is really over-simplified explanation, refer to the link to read more, but thats the gist of it.
So basically, there is no "FIFO" queue here- the async functions order is literally depends on stuff like your processor speed, your operating system, etc.
BUT- there is a way to make sure one async action does get performed only after another one finishes, and this is the .then clause. The thing is, it only assures the specific function inside the .then will be performed after the specific promise it was concatenated to, but it does not say anything about the order of it in regars to other async operations (promises) in the event loop. So for example in your code:
new Promise(resolve => {
resolve() // PROMISE A
})
.then(() => {
new Promise(resolve => {
resolve() // PROMISE B
})
.then(() => {
console.log(1) //PROMISE C
})
.then(() => {
console.log(2)
})
.then(() => {
console.log(3.1)
})
})
.then(() => {
console.log(1.1) // PROMISE D
new Promise((resolve => {
resolve()
}))
I took part of it to explain:
so, Promise A resolves first. this assures that promise B will resolve now. here is when things gets complicated: since promise B is resolved, both promise C and D now get into event loop! why? because Promise A had 2 .then clauses, so when the first one ends- event loop takes the 2nd one which is promise D. but the first .then clause had also a .then clause of his own - promise C, which also enters the event loop.
THERE IS NO CONNECTION BETWEEN PROMISE D AND C! They could be performed in any order. keep that logic and you'll see how it works out for the rest of the promises, and also if you try to run it on different OS it might be that promises order will be different because of different implementations of the OS for the event loop.
Hope this helps you to understand a little.
DISCLAIMER: I have not much experience in JS, but promises really intrigued me so I did a deep research about it. I'm standing behind everything I wrote here, but if there are any corrections to my explanation I'd love to hear!
EDIT
The answer beneath me is also correct but with no explanation, so let me add to it:
When you do not return anything inside a promise (or a .then clause, which also returns a promise), it will implicitly return a resolved promise with no value before going out of the promise, basically like adding a return new Promise.resolve() after teh console.log in promise C, for example. When its done like this, all the .then clauses coming after promise B will only enter the event loop after the previous one ended (e.g b ends, so C goes into loop, then the next .then and so on), but between them other promises or .then clauses (like promise D) can enter as well.
But, when you RETURN the promise that has the .then clauses chained to it- it makes sure the whole block of the promise + then clauses goes into event loop as one in order, so the .then clauses will also be performed in the order you wanted :)
tnx #Eugene Sunic for the addition!

It results in unpredictable order because of un-existing returns in your code.
Adding return to your promises and you'll get comprehensible outputs and can easily track the promises execution.
Firstly, synchronous 0 is printed then the entire first promise block gets executed, like you said FIFO.
1,2, 3.1
After that the chaining thenable gets executed
1.1
After that the block 4,6 gets printed
following the chaining thenable which outputs 5 and at last, the last thenable prints number 3
Leaving us with 0,1,2, 3.1, 1.1, 4,6,5,3
new Promise(resolve => resolve())
.then(() => {
return new Promise(resolve => resolve())
.then(() => console.log(1))
.then(() => console.log(2))
.then(() => console.log(3.1));
})
.then(() => {
console.log(1.1);
return new Promise((resolve => resolve()))
.then(() => {
return new Promise((resolve) => resolve())
.then(() => console.log(4))
.then(() => console.log(6))
}).then(() => console.log(5))
}).then(() => console.log(3))
console.log(0)

It's FIFO and the execution looks like this:
main [4] logs: 0 // main code executed, one executor added to FIFO (4)
4 [8,18] // executor at line 4 runs, two executors added to FIFO (8, 18)
8 [18,11] logs: 1 // etc etc
18 [11,23,36] logs: 1.1
11 [23,36,14] logs: 2
23 [36,14,27,33]
36 [14,27,33] logs: 3
14 [27,33] logs: 3.1
27 [33,30] logs: 4
33 [30] logs: 5
30 logs: 6
as you can see its first in first out order: [4,8,18,11,23,36,14,27,33,30] but it stores executors (callbacks for promises that were fulfilled or rejected), not promises. In other words: the time when promise is fulfilled or rejected decides when its added to FIFO not the time the promise is created.

Related

Determine promise state using Promise.race and setImmediate

TL;DR: Will already resolved promises always beat setImmediate in a race?
Background:
Sometimes you want to know if a promise is resolved without awaiting its completion. There are some old legacy tricks like using util.inspect to get the internal state and checking if it contains the string "<pending>". However, a more stable solution is to use Promise.race() to essentially wait for your unknown promise with a (very small) timeout.
const p = doSomething();
const result = await Promise.race([
p,
new Promise(resolve => setTimeout(resolve, 1))
]);
if (result) {
// p was resolved within 1 ms!
...
}
This will at most wait 1 ms to get result, which will then either contain the resolved value of p or undefined.
If required, the "timeout promise" may of course return something different than undefined to distinguish actual undefined values returned from doSomething():
const PENDING = Symbol.for('PENDING');
const result = await Promise.race([
p,
new Promise(resolve => setTimeout(() => resolve(PENDING), 1))
]);
if (result !== PENDING) {
...
}
Now we'll either get the resolved value from doSomething() or the unique symbol PENDING.
Now to my question. In addition to setTimeout, there's a setImmediate function that basically behaves like a timer that expires immediately; it just gives the event loop a go before resolving. When using this in my Promise.race expression above, empirically it seems to work, i.e., if p is already resolved it will beat setImmediate in a race, but I'm wondering if there is any such guarantee - or if I should be using a timer of 1 ms to guarantee that a resolved promise beats the timer?
I've tried putting p before and after the setImmediate promise in the array passed to Promise.race and it worked both ways, but still I'm worried it might behave randomly or be OS dependent? Or is the fact that setImmediate waits for one round of I/O enough to guarantee that any resolved promises will win?
From the documentation:
Schedules the "immediate" execution of the callback after I/O events' callbacks
Edit:
I found that even this "seems" to work:
const result = await Promise.race([p, new Promise(resolve => resolve(PENDING))]);
or actually even this:
const result = await Promise.race([p, Promise.resolve(PENDING)]);
However, here the order is important. If p is resolved and is before the timeout promise, it will win, but if it's after the timeout promise in the array it will lose.
The question is the same though: is this approach guaranteed to let p win if it's already resolved?
Considering the codes below in Nodejs
Ref. https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
setImmediate(() => console.log("setImmediate"));
setTimeout(() => console.log("setTimeout"));
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(() => console.log("Promise"));
console.log("sync");
Output:
You can notice the sequence and that's how it is executed in order.
sync
nextTick
Promise
setTimeout
setImmediate
To answer your question, we can wrap the codes in Promises like below:
(async function main() {
const result = await Promise.race([
new Promise(resolve => resolve("sync")),
new Promise(resolve => setImmediate(() => resolve("setImmediate"))),
new Promise(resolve => setTimeout(() => resolve("setTimeout"))),
new Promise(resolve => Promise.resolve().then(() => resolve("Promise"))),
new Promise(resolve => process.nextTick(() => resolve("nextTick"))),
]);
console.log({ result });
})();
As sync function is to be executed first, so it would be returned.
Output:
{ result: 'sync' }
You can comment one of the Promise above to see which one resolve first.

NodeJS getting Promise result from Console

If I want to get the result of a Promise from my node-testing console, how would I do that?
eg.
let promise = new Promise(() => {
console.log('my promise');
}
promise.then(() => { console.log('resolved') })
// returns a Promise {<pending>}
await promise.then(() => { console.log('resolved') })
// still returns just a Promise
(async () => {
await promise
})()
// still returns ... just a Promise
Ultimately I'm trying to test promise results (database queries) from my node console at a breakpoint, and I just keep getting Promises returned.
UPDATE - I guess this is more complicated than I thought. Because know one has been able to answer.
I understand how to get the results of a promise in a regular environment. I'm talking about getting results at a breakpoint while debugging a Node application. To connect to the console I'm referring to please follow these directions:
https://medium.com/#paul_irish/debugging-node-js-nightlies-with-chrome-devtools-7c4a1b95ae27
From the console in DevTools, promises keep returning Promise {}. I do not know if it is possible to get the value of the promise, but if anyone knows either way please let me know.
when you create a promise object you should use this syntax
var promiseObj = new Promise(executor);
the executor is a function with this signature
function(resolutionFunc, rejectionFunc){
// typically, some asynchronous operation.
}
When we go back to your specific example, you can define it as
let promise = new Promise( resolve => {
resolve("my promise")
})
Note I havent added the reject function
then you can do
promise.then(value => console.log(value))
you can find a detailed description here
From the comments I guess it is impossible to get asynchronous call results while at a breakpoint in JavaScript.
If anyone comes here and wants a way to be able to make DB queries from the Node console (REPL) like I was looking for, I'd recommend going here:
https://medium.com/#vemarav/build-rails-like-console-in-nodejs-repl-2459fb5d387b
The position of your breakpoints matters, and the behavior will be the same in the Node.js debugger and the browser debugger.
In the following I use labels like <b1> to identify breakpoint positions.
1. const p1 = new Promise((resolve) => <b1> resolve('result 1'))
2. <b2>
3. const p2 = p1.then(() => <b3> 'result 2')
4. <b4>
At <b1>, p1 will be undeclared because the executor function runs synchronously, and the variable declaration process has not yet completed.
At <b2>, p1 will be a fulfilled promise (Promise {<fulfilled>: "result 1"}), resolved with the value 'result 1' because resolve was called in the executor function.
The next breakpoint to be hit will be <b4>. Note: not <b3>.
At <b4>, p2 will be a pending promise (Promise {<pending>}) because the promise has been configured with a .then that has not yet had the opportunity to run. .then callbacks are run on asynchronous microtasks to give the programmer the opportunity to configure a promise chain ahead of its execution. After all, promises are designed to be used with asynchronous behavior.
On the next available microtask, <b3> will be hit as the .then callback is run. At <b3> the values of p1 and p2 remain unchanged. p1 was fulfilled earlier and will not change. The state of p2 is unchanged because the .then it was configured with has not yet completed running.
In order to observe p2 in its fulfilled state, you need to add a breakpoint to an extension of the promise chain.
let p1 = new Promise((resolve) => resolve('result 1'))
const p2 = p1.then(() => 'result 2')
p2.then(() => {
// here `p2` === `Promise {<resolved>: "result 2"}`
console.log(p2)
})

Execution order in Promise()

Program - 1
new Promise(resolve => {
resolve('1');
Promise.resolve().then(() => console.log('2'));
}).then(data => {
console.log(data);
}); // output 2 1
Program -2
new Promise(resolve => {
Promise.resolve().then(() => {
resolve('1');
Promise.resolve().then(() => console.log('2'));
});
}).then(data => {
console.log(data);
}); // output 1 2
I am really confused with the output of both the programs. Can anyone please tell me how the execution thread works here?
What is hard to understand is that resolve doesn't work the same way as return. Creating Promises actually creates a new async context. Everything inside the first function of the Promise is executed synchronously. Then the .then() methods are called with the values resolved synchronously or asynchronously.
In the first example, you resolve '1', but then you create a new Promise with Promise.resolve() and you call the .then() right away. Because the new Promise is inside the first one, everything is called synchronously.
new Promise(resolve => {
resolve('1'); // Will be accessed in the .then() after then end of the callback
Promise.resolve().then(() => log('2')); // called before the end of the function
// because the context is not yet created
}).then(data => {
log(data);
}); // output 2 1
The second example is way harder. You actually create a Promise inside the first Promise and call its resolve right away. The order of execution will be:
first everything in the initial callbacks
After that, create sort of eventListeners from the resolve to the .then
When a resolve is called, execute the callback in .then
There is nothing in the initial function of the nested promise. So it goes straight to the then. The .then() calls the initial resolve(). Here, the kind of eventListener is created so we jump to the initial callback. Then we go back and execute the rest of the function.
new Promise(resolve => {
// Because we call resolve right away, the context of the original promise
// is created
Promise.resolve().then(() => {
resolve('1'); // Calling resolve jumps us to the .then
Promise.resolve().then(() => log('2')); // We execute after.then
});
}).then(data => {
log(data);
}); // output 1 2
If you removed the .resolve(), it would work like the first one:
new Promise(resolve => {
new Promise(() => {
resolve('1');
Promise.resolve().then(() => log('2'));
});
}).then(data => {
log(data);
}); // output 2 1
The difference lies in the context that the event loop is in when resolving the promise (resolve(1)).
There are 2 queues that execute code in JS:
microtask Q
macrotask Q
JS executes a microtask and then runs all the tasks from the macrotask Q (if any)
When resolving a promise from the executor (Promise(...)) you are running inside the macrotask Q. Code executed from inside a promise callback is executed on the microtask Q.
The difference that matters in this case is that when running from inside the microtask Q if you add other microtask (promise callbacks are microtasks) they get added to the current Q and get processed during this queue run.
This is what happens in case no 2, you are resolving the promise from inside a microtask Q, this ends up resolving the top level Promise and add the .then(data => { log(data); }); to the current microtask Q. So the callback will get executed in this run. On the other hand, the handlers of the nested promise Promise.resolve() is not executed now, as handlers are always called async.
Note: it is possible to add microtasks ad infinitum from inside the microtask Q, blocking the execution.

Hard time understanding difference between Promise.resolve() and promise chain

I have some hard time understanding what is the difference between using Promise.resolve() and simple using the promise.
The explanation seems a bit hard to get so i have a little example:
For example, i have a method that return a promise like this:
requestPokemon() {
return new Promise((resolve, reject) => {
axios.get("https://pokeapi.co/api/v2/pokemon/1").then(value => {
resolve(value);
})
.catch(error => {
reject(error);
})
});
}
so, now i can call this method and chain the promise, i figure two ways of do it, and can't get it when Promise.resolve is better or not, my issue is understanding that.
so i solved it in two ways:
first:
Promise.resolve(this.requestPokemon()).then(value => {
this.pokemon = value.data.name;
}).catch(error => {
console.log(error);
})
second:
this.requestPokemon().then(value => {
this.pokemon = value.data.name;
}).catch(error => {
console.log(error);
})
please i need a little explanation of the downsides and upsides on doing it one way above the other, i appreciate the help a lot.
Thanks
At first you dont need to construct a promise (thats an antipattern) in your requestPromise, just return the one from axios:
requestPokemon() {
return axios.get("https://pokeapi.co/api/v2/pokemon/1");
}
Now lets have a look at this line:
Promise.resolve(this.requestPokemon()).then(/*...*/)
It will create a new promise, that resolves or rejects when the axios promise resolves or rejects and adds a then handler to it. So it will be chained like this like this:
resolve() -> Promise (axios) -> Promise (Promise.resolve) -> then
reject() -> Promise (axios) -> Promise (Promise.resolve) -> catch
So as you can see Promise.resolve just passes through the data, so actually it is superflous. We actually just need:
resolve() -> Promise (axios) -> then
reject() -> Promise (axios) -> catch
Which can be done with:
this.requestPokemon().then(/*...*/, /*...*/);
please i need a little explanation of the downsides and upsides on doing it one way above the other
Adding Promise.resolve is just a bit more typing and makes it less readable, there is no syntactical difference.

Does promise-chaining guarantee that a callback of the next promise is called only after preceding one is finished?

I met an article about promises in js, where the author shows a piece of code:
// I want to remove() all docs
db.allDocs({include_docs: true}).then(function (result) {
result.rows.forEach(function (row) {
db.remove(row.doc);
});
}).then(function () {
// I naively believe all docs have been removed() now!
});
and says
What's the problem with this code? The problem is that the first
function is actually returning undefined, meaning that the second
function isn't waiting for db.remove() to be called on all the
documents. In fact, it isn't waiting on anything, and can execute when
any number of docs have been removed!
So, as I understood, if the second callback function() {} accepts no arguments, it actually doesn't wait for the end of the callback function(result). Am I inferring correctly? Because when I run the following piece of code, it gives me an opposite result:
var array = [];
for ( let i = 0; i < 1000; i++ )
array.push(i);
var promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(), 1000);
});
promise
.then(() => array.forEach(item => console.log(item)))
.then(() => console.log("invoked earlier"));
I wait for "invoked earlier" to appear in the middle of printed numbers. But doesn't matter how large the number of items is. "Invoked earlier" appears always at the end. Can someone explain me what I am missing? Maybe the article is outdated and something has changed since then?
In order to guarantee this, you actually have to return promises from the previous promise.
Whatever you return from a promise will be passed into the next promise.
In your case, you're returning undefined.
If, instead, you returned a promise, then the promise would be resolved, and the second promise would run after that happened, and would be passed whatever value the promise resolved with.
So yes, promises are guaranteed to run one after another, but if you choose to do something async inside of their callback, and don't bother chaining it back into the promise by returning it, then they're not going to bother to wait for anything (because they don't know that there's anything to wait for).
I'm assuming db.remove returns a promise...
...so, knowing that we have to return a promise from the callback, in order for it to actually wait for async stuff to happen, I would do something like this.
.then(result => Promise.all(result.rows.map(row => db.remove(row.doc))))
.then((listOfRemoves) => console.log(`removed`));
Whether the second function takes 1 argument or 0 arguments is 100% inconsequential as to when the second function runs.
Edit
examples:
.then(() => setTimeout(() => console.log('2nd', 20000)))
.then(() => console.log('first'));
This happens because the first then has NO idea that there is a setTimeout happening. It's not a mind-reader, it just runs the code and passes the return value along (in this case, undefined).
If the return value happens to be a promise, though, it will wait until that promise is done, get the value from it, and pass that on.
.then(() => new Promise(resolve => setTimeout(resolve, 20000)))
.then(() => console.log('I waited 20 seconds.'));
Because it's returning a promise, it will call the then of that promise, and wait to get the value, to pass on.
The reason your tests are failing is because you're basically doing this.
.then(() => console.log('first'))
.then(() => console.log('second'));
These are guaranteed to fire in this order. Period.
All of the other ones have been firing in that order as well. It's just that they are also scheduling async processes, just like all other callbacks/timeouts that use async I/O in JS.

Categories

Resources