How can I synchronously determine a JavaScript Promise's state?


#1

I asked this question on StackOverflow.

I have a pure JavaScript Promise (built-in implementation or poly-fill):

var promise = new Promise(function (resolve, reject) { /* ... */ });

From the specification, a Promise can be one of:

  • ‘settled’ and ‘resolved’
  • ‘settled’ and ‘rejected’
  • ‘pending’

I have a use case where I wish to interrogate the Promise synchronously and determine:

  • is the Promise settled?
  • if so, is the Promise resolved?

I know that I can use #then() to schedule work to be performed asynchronously after the Promise changes state. I am NOT asking how to do this.

This question is specifically about synchronous interrogation of a Promise’s state. How can I achieve this?

There is apparently no API for this as part of the specification for native Promises.

Some user-land implementations of Promises do offer this (e.g. jQuery). Can we please get a standard API for this purpose in native Promise?


#2

To get a standard API for this, you’ll need to convince the JavaScript standard committee, as well as the browser vendors, that your use case is widespread and important enough to be worth the standardization and implementation burden, and that it cannot be achieved in any other possible way.

So … go!


#3

Here’s the discussion on the ES Discuss mailing list: https://esdiscuss.org/topic/how-can-i-synchronously-determine-a-javascript-promise-s-state


#4

I think I replied to the mailing list, but I doubt I did it correctly:

legacy use case

I am maintaining an existing API that includes asynchronous functions (mix of callbacks and Promises) and synchronous functions. After some asynchronous initialisation, the internal state settles and it is perfectly safe to use the synchronous functions as expected.

So, I’d like to emit warnings when these synchronous functions are called prior to a Promise being “settled”. That way, downstream developers will know that they should be waiting for the Promise to settle before using such functions.

This actually isn’t too different to the XHR / Fetch APIs conceptually. We get the ball rolling with an asynchronous API call, but there are deterministic blocks within which we can synchronously interrogate progress, etc.

activity indicator use case

I use a Promise to represent a network transaction. I wish to alter the visual state of my web app to reflect the state of this network transaction. I can, for example, show an indeterminate progress bar whilst the Promise is not “settled”.

If I am using requestAnimationFrame, or a framework like React, then the state would be synchronously mapped to the DOM / canvas during each execution of my render function.

I can track the state of the Promise using additional variables (as others have suggested), but those state values already exist somewhere private per the functioning of a Promise. I’d be duplicating work that the JavaScript engine is already performing internally, at the risk of introducing errors in my code.

third-party popular libraries

The following libraries implement some form of Promise and all expose such synchronous inspection capabilities:


#5

Implementing a performant promisified reduce/fold function requires synchronous state inspection.

Say I implement a reduce function with signature:

Promise.reduce(array, accumulator, fn)

where fn is of the form:

function(accumulator, item) { return promise_or_value; }
  • If fn returns a value then reduce can immediately continue with the next item.
  • If fn returns a promise then reduce will defer processing through a .then() call, even if the promise is already fulfilled.
  • If fn returns a promise-with-sync-state-inspection then reduce can immediately continue with the next item (assuming the promise already fulfilled).

Some primitive perf testing based on:

Promise.reduce(new Array(10000), null, function(dummy, item) { 
    return Promise.resolve(item); 
})

Results can be a bit variable because deferring is unpredictable, but the following are reasonably representative on my (otherwise idling) desktop:

  • Standard promises: Chrome: 1200ms, Firefox: 600ms
  • Promises with sync inspection: Chrome: 150ms, Firefox: 200ms

The promises with sync inspection would be even faster but the code honors a 12ms task limit. Probably a large part of the difference is that with standard promises reduce() requires the creation of an additional promise every time the callback returns a promise.