Reputation: 1150
It is known one can combine multiple Promises into a chain and thereby call onFulfilled
callbacks exactly one by one (despite they will be asynchronous from each other):
Promise.resolve()
.then(() => {
console.log("i will be the first one");
})
.then(() => {
console.log("i will be the second one"); // definetely second
})
;
But what about non-chained subsequent Promises? The simplest example:
Promise.resolve()
.then(() => console.log("i will be the first one"))
;
Promise.resolve()
.then(() => console.log("i will be the second one")) // sure?
;
In my naive opinion, Promises callbacks works via event queue (about like timer event inside setTimeout
) and in that way first Promise.resolve()
push its event in queue before second one do this, therefore first callback will be called before second one.
But I am not sure there are any guarantees about it. Can I rely on it or it is an async lotto? Does someone know what specs tell about it?
UPDATE
Some of you noticed is the simplest example is useless so I want to explain my original problem.
I have a class which lazy initializes instance of another class and provides get method for the hosted instance:
class Lazy {
/** @param {Class} T */
constructor(T) { }
/** @returns {Promise.<T>} */
instance() {
// there will be complex async initialization at first call
// and Promise.resolve() at following calls
}
}
class Foo {
on() { }
off() { }
}
/** @type {Lazy.<Foo>} */
let foo = new Lazy(Foo);
foo.instance().then((i) => i.on());
foo.instance().then((i) => i.off());
Last two lines reveal my problem - it is difficult to work withFoo
instance in that way when I am not sure that on()
will be called before off()
.
Upvotes: 1
Views: 58
Reputation: 29750
Promises callbacks works via event queue
This isn't true. Callbacks work on a first returned first served basis. The ordering of when you initiate the operation is unimportant, it's the order that they return that decides which operation will be processed first.
In your example if your "first" async operation takes longer to return than your "second" async operation then the "second" then
will be processed first.
Obviously there are lots of variables on how long these operation will take to return. Network speed, load on the server (or whatever async service your using), the browsers implementation of the promise engine, etc. etc. So you have to assume that you have no idea when these will be processed.
To avoid race conditions if you need something to be in a specific order then use callbacks/.then
/await
/etc to ensure that they run in that order. You cannot rely on the order that you call the operation.
Upvotes: 1
Reputation: 276506
Last two lines reveal my problem - it is difficult to work withFoo instance in that way when I am not sure that on() will be called before off().
You should not rely on this behaviour, instead you should await
for obtaining the instance and then cache it:
async function withInstance() {
let instance = await foo.instance();
await instance.on();
await instance.off(); // guaranteed to be called after `.on`.
}
You can rely on execution order of Job
s.
When a promise then
is added it's an EnqueueJob
in the spec. Quoting Jobs and Job Queues:
The PendingJob records from a single Job Queue are always initiated in FIFO order.
Note this guarantee does not hold if you have multiple contexts (for example - different promises between iframes or workers).
That said - it is highly not recommended to rely on the execution order like that.
Upvotes: 1