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?