The idea is something like this:
const result = Promise.resolveIfNested(value, () => scheduleRedraw)
const isPromise = !Object.is(value, result)
This operation would be basically this, but without the duplicate .then
access and with the callback invoked synchronously (and not scheduled as a .then
callback):
Promise.resolveIfNested = (value, onSettled) =>
value != null && typeof value.then === "function"
? Promise.resolve(value).finally(onSettled)
: value
This is a surprisingly common need in libraries and frameworks that have to deal with possibly-async return values, as detecting the difference could yield substantial performance gains in many code bases. For two examples:
- Mithril does a lot of conditional logic to try to avoid deferring removal, as that would result in a frame-long delay before it's actually removed. (Per HTML spec, no microtask checkpoint is invoked between animation frame callbacks and frame rendering.)
-
Mocha checks it to verify that a test body does not both call
done()
and return a promise. - If you want a truly accurate workaround to capture timings, it is far from trivial as you're basically replicating what the engine already implements natively. (This is a major reason why I want the
onSettled
method to be invoked synchronously.) I only implemented this mess to capture timings, not merely for optimization.
You can see the large amount of boilerplate involved with all of them, and you can also see that the first two examples aren't even handling recursion correctly. My third example is monstrous in comparison, just to handle recursion and errors properly, and this proposal would trivialize that using machinery that already largely exists in the spec and in engines.
As for what runtime impact this proposal could have:
- Engines would have exactly one less polymorphic code path to fuss with, yielding performance wins for frameworks that employ it both at baseline and in inlined JIT code.
- Implementation-wise, it's literally the same as
Promise.resolve(value)
, except instead of creating a promise to wrapvalue
, it just returnsvalue
directly.
Anyone else interested?