jokeyrhyme
jokeyrhyme

Reputation: 3668

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

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:

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

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?

Upvotes: 280

Views: 171693

Answers (30)

mpen
mpen

Reputation: 283193

Similar to John's answer, but allows you to get the promise back out too:

export enum PromiseStatus {
    PENDING = 'pending',
    FULFILLED = 'fulfilled',
    REJECTED = 'rejected',
}

export class PromiseMonitor<V,E=unknown> {
    private readonly _promise: Promise<V>
    private _status = PromiseStatus.PENDING
    private _value: V|undefined
    private _reason: E|undefined

    constructor(promise: Promise<V>) {
        this._promise = promise.then(value => {
            this._status = PromiseStatus.FULFILLED
            this._value = value
            return Promise.resolve(value)
        }, reason => {
            this._status = PromiseStatus.REJECTED
            this._reason = reason
            return Promise.reject(reason)
        })
    }

    get status() {
        return this._status
    }

    get value() {
        return this._value
    }

    get reason() {
        return this._reason
    }

    get promise() {
        return this._promise
    }
}

Then you do

const monitor = new MonitorPromise(promise);

And you can use monitor.status to get the current status synchronously at any point, get the value out with monitor.value and if you need to wait for the promise it's available with monitor.promise

Upvotes: 0

ITEnthusiasm
ITEnthusiasm

Reputation: 116

There are a number of rather complex answers here -- likely because this question was asked before we had better support for ES6 Classes and Private Fields. Now that modern browsers support these features, I have a simple, modern, native/package-free solution below. (Note that, as Benjamin Gruenbaum said, a solution is not possible with the Promise class alone; so we need to extend the class to get what we want.)

class WatchablePromise extends Promise {
  #settled = false;
  #status = "pending";

  constructor(executor) {
    super((resolve, reject) => {
      executor(
        (value) => {
          resolve(value);
          this.#settled = true;
          this.#status = "fulfilled";
        },
        (reason) => {
          reject(reason);
          this.#settled = true;
          this.#status = "rejected";
        },
      );
    });
  }

  get settled() {
    return this.#settled;
  }

  get status() {
    return this.#status;
  }
}

async function testCustomPromise() {
  const promise = new WatchablePromise((resolve) => setTimeout(resolve, 1000));
  promise.then(() => console.log("Settled: ", promise.settled)); // Settled: true

  await promise;
  console.log("Status: ", promise.status); // Status: fulfilled
}

testCustomPromise();

Obviously, you can customize this further according to your needs. The one caveat is that you'll need to use this custom class instead of using the regular Promise class whenever you want to spy on the details of the promise. But hopefully the number of times you'll need to do this will be minimal. I only came up with this solution because I'm trying to test a library that I'm working on.


For TS users, it's important to note that TypeScript's definition of a Promise is rather complex because it defines both an interface type and a constructor type. So I had to reuse what they define on PromiseConstructor and come up with something like this:

class WatchablePromise<T> extends Promise<T> {
  #settled = false;
  #status: "pending" | "fulfilled" | "rejected" = "pending";

  constructor(
    executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: unknown) => void) => void
  ) {
    super((resolve, reject) => {
      executor(
        (value) => {
          resolve(value);
          this.#settled = true;
          this.#status = "fulfilled";
        },
        (reason) => {
          reject(reason);
          this.#settled = true;
          this.#status = "rejected";
        }
      );
    });
  }

  get settled() {
    return this.#settled;
  }

  get status() {
    return this.#status;
  }
}

Upvotes: 1

user9987636
user9987636

Reputation: 49


type PromiseState =
  | 'pending'
  | 'fulfilled'
  | 'rejected';

export async function getPromiseState<T>(promise: Promise<T>): Promise<PromiseState> {
  return await Promise.race([
    promise.then(() => 'fulfilled', () => 'rejected'),
    new Promise<PromiseState>(() => 'pending'),
  ]);
}

Upvotes: -1

Redu
Redu

Reputation: 26191

If you want to create your own observable promises (OP class) then this functionality is rather simple. Plus you can even prematuraly resolve or reject it externally because I expose the resolve and reject functions as well. This means you can abort a promise whenever you want by calling it's .reject(e) method. Obviously this is not inline with the Promise specificiations A+. Yet, while we don't extend the Promise class, as a draft it should still be compatible with the native promises and async-await. Since this eliminates the well thought precautions of async operations, you should be careful when using abstractions like this one.

I have also added some extra functionalities like wait and log to show how easy it would be to chain up custom async tasks in a sane manner.

class OP {
  #p;
  #r;
  #s;
  #v;
  #x;
  static resolve = r => new OP(v => v(r));
  static reject  = e => new OP((_,x) => x(e));
  constructor(cb) {
    this.#p = new Promise((v,x) => ( this.#v = v
                                   , this.#x = x
                                   , cb && cb( r => this.resolve(r)
                                             , e => this.reject(e)
                                             )
                                   ));
    this.#s = "pending";
    this.#r;
  }
  
  get status(){
    return this.#s;
  }

  get result(){
    return this.#r;
  }

  resolve(r){
    this.#v(r);
    this.#s === "pending" && ( this.#s = "resolved"
                             , this.#r = r
                             );
  }

  reject(e){
    this.#x(e);
    this.#s === "pending" && ( this.#s = "rejected"
                             , this.#r = e
                             );   
  }
  
  then(onFulfilled,onRejected){
    return new OP((v,x) => this.#p.then( r => v(onFulfilled ? onFulfilled(r) : r)
                                        , e => v(onRejected ? onRejected(e) : e)
                                        ));
  }
  catch(onRejected){
    return new OP((v,x) => this.#p.catch(e => v(onRejected ? onRejected(e) : e)));
  }
  finally(onFinally){
    return new OP((v,x) => this.#p.finally(_ => v(onFinally ? onFinally() : void 0)));
  }
  
  wait(ms){
      return this.then(r => new OP((v,x) => setTimeout(v,ms,r)))
  }
  log(str){
      return this.then(r => (console.log(str ? str : r),r));
  }
}


var p = new OP((v,x) => setTimeout(v,1000,"OP works..!")),
    q = new OP((v,x) => setTimeout(v,2000,"OP works also with Promise methods..!"));
setTimeout(_ => console.log(p.status),500);
setTimeout(_ => console.log(p.status),1500);
p.then(str => console.log(str))
 .catch(err => console.log(err));

Promise.all([p,q]).then(rs => rs.forEach(r => console.log(r)));

p.then(str => (console.log(str),str)).then(str => console.log("once again",str));

async function tester(){
  if (Math.random() < 0.5) return await OP.reject("oops the async function threw..!");
  return await new OP((v,x) => setTimeout(v,3000,"OP works with await..!"));
}

tester().then(s => console.log(s)).catch(e => console.log(e));
OP.resolve()
  .wait(4000)
  .log("Hello")
  .wait(1000)
  .log("there")
  .wait(1000)
  .log("something useful..!");

Upvotes: 0

BenVida
BenVida

Reputation: 2312

It looks like somehow nobody came up with one of the simplest solution that doesn't require any hacks:

  • define a variable to indicate that the promise is running
  • Add a .finally clause to the promise that sets the variable to false (you can do it at any time after the promise is created)
  • After that in your code just check if the above variable is true or false, to see whether the Promise is still running.

If you want to know not just whether it's finished or not then instead of .finally add a .then and a .catch clauses that set the variable to "resolved" or "rejected".

The only drawback is that the state variable doesn't get set right away (synchronously) when you add the clauses, in case the promise has already finished. Because of this, it's best to add this to the earliest possible place after the creation of the promise.

Example:

async function worker(){
  // wait a very short period of time
  await (new Promise(resolve => setTimeout(resolve, 100)))
  //...
}

const w1=worker()


let w1_running=true
w1.finally( ()=> {w1_running=false});

//...
//Then check if it's running

(async ()=>{
  while(true){
    if (w1_running) {
      console.log("Still Busy :(")
    } else {
      console.log("All done :)")
      break
    }
    await (new Promise(resolve => setTimeout(resolve, 10)))
  }
})()

// Note we need some async action started otherwise the event loop would never reach the code in the function `worker` or in the `.finally` clause

Upvotes: 2

Sindre Sorhus
Sindre Sorhus

Reputation: 63487

I made a package for this. Unlike most of the other answers here, it doesn't swallow unhandled rejections.

npm install p-state
import timers from 'timers/promises';
import {promiseStateSync} from 'p-state';

const timeoutPromise = timers.setTimeout(100);

console.log(promiseStateSync(timeoutPromise));
//=> 'pending'

await timeoutPromise;

console.log(promiseStateSync(timeoutPromise));
//=> 'fulfilled'

Upvotes: 3

Mir-Ismaili
Mir-Ismaili

Reputation: 17184

CAVEAT: process.binding('util').getPromiseDetails is undefined on node 16!

Benchmark:

Candidates:

/**
 * https://stackoverflow.com/a/47009572/5318303
 */
const isPromisePending1 = (() => { // noinspection JSUnresolvedFunction
    const util = process.binding('util')  // noinspection JSUnresolvedFunction
    return promise => !util.getPromiseDetails(promise)[0]
})()

/**
 * https://stackoverflow.com/a/35852666/5318303
 */
const isPromisePending2 = (promise) => util.inspect(promise) === 'Promise { <pending> }'

/**
 * https://stackoverflow.com/a/35820220/5318303
 */
const isPromisePending3 = (promise) => {
    const t = {}
    return Promise.race([promise, t])
            .then(v => v === t, () => false)
}

Test promises:

const a = Promise.resolve()
const b = Promise.reject()
const c = new Promise(() => {})
const x = (async () => 1)()

Run benchmark:

const n = 1000000

console.time('isPromisePending1')
for (let i = 0; i < n; i++) {
    isPromisePending1(a)
    isPromisePending1(b)
    isPromisePending1(c)
    isPromisePending1(x)
}
console.timeEnd('isPromisePending1')

console.time('isPromisePending2')
for (let i = 0; i < n; i++) {
    isPromisePending2(a)
    isPromisePending2(b)
    isPromisePending2(c)
    isPromisePending2(x)
}
console.timeEnd('isPromisePending2')

console.time('isPromisePending3')
for (let i = 0; i < n; i++) {
    await isPromisePending3(a)
    await isPromisePending3(b)
    await isPromisePending3(c)
    await isPromisePending3(x)
}
console.timeEnd('isPromisePending3')

Result:

isPromisePending1: 440.694ms
isPromisePending2: 3.354s
isPromisePending3: 4.761s

Obviously isPromisePending1() is too faster (8~10 times)! But it's not usable on node 16! (see above caveat).

Upvotes: 1

smallscript
smallscript

Reputation: 730

This is the Future pattern I use: (https://github.com/Smallscript-Corp)

  • enables sync and async fn usage
  • enables event patterns to be unified with async behavior
class XPromise extends Promise {
  state = 'pending'
  get settled() {return(this.state !== 'pending')}
  resolve(v,...a) {
    this.state = 'resolved'
    return(this.resolve_(this.value = v,...a))
  }
  reject(e,...a) {
    this.state = 'rejected'
    return(this.reject_(this.value = (e instanceof Error) ? e : XPromise.Error(e),...a))
  }
  static Error(e) {const v = Error('value-rejected'); v.value = e; return(v)}
  static Future(fn,...args) { // FactoryFn
    let r,t,fv = new XPromise((r_,t_) => {r=r_;t=t_})
    fv.resolve_ = r; fv.reject_  = t;
    switch(typeof fn) {
      case 'undefined': break; case 'function': fn(fv,...args); break;
      default: fv.resolve(fn)
    }
    return(fv)
  }
}
global.Future = XPromise.Future

Then you can create future-value instances that can be resolved using sync and async functions; enables handling events uniformly.

You can use it to write a pattern like:

async doSomething() {
  // Start both - logically async-parallel
  const fvIsNetworkOnLine = this.fvIsNetworkOnline
  const fvAuthToken = this.fvAuthToken
  // await both (order not critical since both started/queued above)
  await fvAuthToken
  await fvIsNetworkOnLine
  // ... we can check the future values here if needed `fv.resolved`, `fv.state` etc
  // ... do dependent workflow here ...
}
onNetworkOnLine(fIsOnline) {
  // We utilize the `fv.settled` below, and use the event to `settle` it etc
  if(fIsOnline) {
    if(this.fvNetworkAvailable_)
      this.fvNetworkAvailable_.resolve(true)
    this.fvNetworkAvailable_ = undefined
  }
  else if(this.fvNetworkAvailable_.settled) {
    this.fvNetworkAvailable_ = undefined
  }
}
get fvNetworkAvailable() {
  if(navigator.onLine)
    return true
  else if(this.fvNetworkAvailable_)
    return this.fvNetworkAvailable_
  return (this.fvNetworkAvailable_ = Future())
}
get fvAuthToken() {
  if(this.fvAuthToken_)
    return this.fvAuthToken_
  const authTokenFv = async fv => {
    // ... handle retry logic etc here ...
  }
  return(this.fvAuthToken_ = Future(authTokenFv))
}

Upvotes: 0

JohnRC
JohnRC

Reputation: 1371

I looked through the solutions proposed to this question and could not see one that corresponds to a simple approach that I have used in Node.js.

I have defined a simple class PromiseMonitor, which takes a promise as the single parameter to its constructor, and has a string property .status which returns the standard string values corresponding to the promise status, "pending", "resolved" or "rejected", and four boolean properties .pending, .resolved, .rejected and .error. The property .error is set true only if .rejected is true and the reject callback was passed an Error object.

The class simply uses .then() on the promise to change the status of the PromiseMonitor when the promise is resolved or rejected. It does not interfere with any other use of the original promise. Here is the code:

class PromiseMonitor {
    constructor(prm){
        this._status = "pending";
        this._pending = true;
        this._resolved = false;
        this._rejected = false;
        this._error = false;
        prm
            .then( ()=>{  
                        this._status = "resolved"; 
                        this._resolved = true; 
                        this._pending = false; 
                    } 
                , (err)=>{ 
                        this._status = "rejected";
                        this._pending = false;
                        this._rejected = true;
                        this._error = err instanceof Error ? true: false ; 
                    } 
                );
    }

    get status(){ return this._status; };
    get pending(){ return this._pending; };
    get resolved(){ return this._resolved; };
    get rejected(){ return this._rejected; };
    get error(){ return this._error };
};

To monitor the status of a Promise, simply create an instance of PromiseMonitor, passing the promise in as a parameter, for example:

let promiseObject = functionThatReturnsAPromise();
let promiseMonitor = new PromiseMonitor( promiseObject );

Now you can syncrhonously check all the properties of promiseMonitor, which will track the status of the original promise. Here is a test script that demonstrates the three possible resolutions of a promise being monitored.

let ticks = 0;
let tickerID = setInterval( ()=>{++ticks; console.log(`..tick ${ticks}`)}, 1000);

async function run(){
    console.log("Start");

    let delay = prmDelay(2000);
    let delayMonitor = new PromiseMonitor(delay);

    // normal handling of delay promise
    delay.then((result)=>( console.log("Normal resolution of delay using .then()") ) );

    console.log("delay at start:\n", delay);
    console.log("delayMonitor at start:\n", delayMonitor);
    await delay;
    console.log("delay finished:\n", delay);
    console.log("delayMonitor finished:\n", delayMonitor);


    console.log("\n\n TEST2: Rejection without an Error test ================================")
    let rejDelay = prmDelay(3000, "reject");
    let rejMonitor = new PromiseMonitor(rejDelay);

    // normal handling of reject result on promise
    rejDelay.then((result)=>( console.log("Normal resolution of rejDelay using .then will not happen") ) 
                    , (err)=>( console.log("Rejection of rejDelay handled using .then")));

    console.log("rejDelay at start:\n", rejDelay);
    console.log("rejMonitor at start:\n", rejMonitor);

    await rejDelay.catch( (err)=>{ console.log( "Caught error using .catch on rejDelay" ); });

    console.log("rejDelay finished:\n", rejDelay);
    console.log("rejMonitor finished:\n", rejMonitor);


    console.log("\n\n TEST3: Rejection with an Error test ================================")
    let errMonitor ;
    let errDelay;
    try{

        errDelay = prmDelay(1000, "error");
        errMonitor = new PromiseMonitor(errDelay);
        
        // normal handling of results of the original promise
        errDelay.then(
            (result)=>{ 
                console.log("Normal expiry of errDelay");
                console.log("Monitor Status is " + errMonitor.status )
            } 
            , (err)=>{
                console.log("** Rejection of errDelay handled using .then()");
                console.log("   Monitor Status is " + errMonitor.status )
            }
        );

        console.log("errDelay at start:\n", errDelay);
        console.log("errMonitor at start:\n", errMonitor);

        await errDelay;

        console.log("**** This should never be run");

    } catch(err) { 

        console.log( "** Caught error on errDelay using try{}catch{}:" ); 
        console.log( "   Monitor Status is " + errMonitor.status )

    };

    console.log("errDelay finished:\n", errDelay);
    console.log("errMonitor finished:\n", errMonitor);
    

    clearInterval(tickerID);


}

/**
 * Creates a new promise with a specific result
 * @param {*} tt 
 * @param {*} exitType ("resolve", "reject" or "error")
 */
function prmDelay (tt, exitType) {
    
    return new Promise(function(resolve, reject) {
        if( exitType == 'reject' ){
            setTimeout(()=>{ reject("REJECTED")}, tt);
        } else if( exitType== 'error'){
            setTimeout(()=>{ reject(new Error( "ERROR Rejection") ); }, tt);
        } else {
            setTimeout(()=>{ resolve("RESOLVED") }, tt);
        } ;
    });
};


run();

Upvotes: 7

junvar
junvar

Reputation: 11594

Old question with many answers but none seem to suggest what I think is the simplest solution: set a bool indicator on promise resolution/rejection.

class Promise2 {
  constructor(...args) {
    let promise = new Promise(...args);
    promise.then(() => promise._resolved_ = true);
    promise.catch(() => promise._rejected_ = true);
    return promise;
  }
}

let p = new Promise2(r => setTimeout(r, 3000));

setInterval(() => {
  console.log('checking synchronously if p is resolved yet?', p._resolved_);
}, 1000);

Upvotes: 0

devio
devio

Reputation: 155

There's another elegant & hacky way of checking if a promise is still pending just by converting the whole object to string and check it with the help of inspect like this: util.inspect(myPromise).includes("pending").

Tested on Node.js 8,9,10,11,12,13

Here's a full example

const util = require("util")

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

(async ()=>{
  let letmesleep = sleep(3000)
  setInterval(()=>{
    console.log(util.inspect(letmesleep).includes("pending"))
  },1000)
})()

Result:

true
true
false
false
false

Upvotes: 3

UtkarshPramodGupta
UtkarshPramodGupta

Reputation: 8152

You can extend the Promise class to create a new queryable Promise class.

You can create your own subclass, say QueryablePromise, by inheriting from the natively available Promise class, the instances of which would have a status property available on it that you can use to query the status of the promise objects synchronously. An implementation of it can be seen below or refer this for a better explanation.

class QueryablePromise extends Promise {
  constructor (executor) {
    super((resolve, reject) => executor(
      data => {
        resolve(data)
        this._status = 'Resolved'
      },
      err => {
        reject(err)
        this._status = 'Rejected'
      },
    ))
    this._status = 'Pending'
  }

  get status () {
    return this._status
  }
}
 
// Create a promise that resolves after 5 sec 
var myQueryablePromise = new QueryablePromise((resolve, reject) => {
  setTimeout(() => resolve(), 5000)
})

// Log the status of the above promise every 500ms
setInterval(() => {
  console.log(myQueryablePromise.status)
}, 500)

Upvotes: 1

pery mimon
pery mimon

Reputation: 8345

2019:

The simple way to do that as I know is thenable , super thin wrapper around promise or any async job.

const sleep = (t) => new Promise(res => setTimeout(res,t));
const sleeping = sleep(30);

function track(promise){
    let state = 'pending';
    promise = promise.finally( _=> state ='fulfilled');
    return {
        get state(){return state},
        then: promise.then.bind(promise), /*thentable*/
        finally:promise.finally.bind(promise),
        catch:promise.catch.bind(promise),
    }
}


promise = track(sleeping);
console.log(promise.state) // pending

promise.then(function(){
    console.log(promise.state); // fulfilled
})

Upvotes: 1

Valen
Valen

Reputation: 1763

await usage to @jib's answer, with idiomatic prototyping.

Object.defineProperty(Promise.prototype, "state", {
    get: function(){
        const o = {};
        return Promise.race([this, o]).then(
            v => v === o ? "pending" : "resolved",
            () => "rejected");
    }
});

// usage: console.log(await <Your Promise>.state);
(async () => {
    console.log(await Promise.resolve(2).state);  // "resolved"
    console.log(await Promise.reject(0).state);   // "rejected"
    console.log(await new Promise(()=>{}).state); // "pending"
})();

note that this async function execute "almost" immediately like synced function (or actually possibly be instantly).

Upvotes: 8

amara
amara

Reputation: 2286

in node, say undocumented internal process.binding('util').getPromiseDetails(promise)

> process.binding('util').getPromiseDetails(Promise.resolve({data: [1,2,3]}));
[ 1, { data: [ 1, 2, 3 ] } ]

> process.binding('util').getPromiseDetails(Promise.reject(new Error('no')));
[ 2, Error: no ]

> process.binding('util').getPromiseDetails(new Promise((resolve) => {}));
[ 0, <1 empty item> ]

Upvotes: 15

Michael Cole
Michael Cole

Reputation: 16257

Updated: 2019

Bluebird.js offers this: http://bluebirdjs.com/docs/api/isfulfilled.html

var Promise = require("bluebird");
let p = Promise.resolve();
console.log(p.isFulfilled());

If you'd prefer to create your own wrapper, here is a nice blog about it.

Because JavaScript is single-threaded, it's hard to find a common enough use case to justify putting this in the spec. The best place to know if a promise is resolved is in .then(). Testing if a Promise is fullfilled would create a polling loop which is most likely the wrong direction.

async/await is a nice construct if you'd like to reason async code synchronously.

await this();
await that();
return 'success!';

Another useful call is Promise.all()

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

When I first reached for this answer, that is the use case I was looking for.

Upvotes: 9

Scott Rudiger
Scott Rudiger

Reputation: 1310

Caveat: This method uses undocumented Node.js internals and could be changed without warning.

In Node you can synchronously determine a promise's state using process.binding('util').getPromiseDetails(/* promise */);.

This will return:

[0, ] for pending,

[1, /* value */] for fulfilled, or

[2, /* value */] for rejected.

const pending = new Promise(resolve => setTimeout(() => resolve('yakko')));;
const fulfilled = Promise.resolve('wakko');
const rejected = Promise.reject('dot');

[pending, fulfilled, rejected].forEach(promise => {
  console.log(process.binding('util').getPromiseDetails(promise));
});

// pending:   [0, ]
// fulfilled: [1, 'wakko']
// rejected:  [2, 'dot']

Wrapping this into a helper function:

const getStatus = promise => ['pending', 'fulfilled', 'rejected'][
  process.binding('util').getPromiseDetails(promise)[0]
];

getStatus(pending); // pending
getStatus(fulfilled); // fulfilled
getStatus(rejected); // rejected

Upvotes: 2

synthet1c
synthet1c

Reputation: 6282

Here is a more fleshed out es6 version of the QueryablePromise, allowing the ability to chain then and catch after the first resolve and to immediately resolve or reject to keep the api consistent with the native Promise.

const PROMISE = Symbol('PROMISE')
const tap = fn => x => (fn(x), x)
const trace = label => tap(x => console.log(label, x))

class QueryablePromise {
  resolved = false
  rejected = false
  fulfilled = false
  catchFns = []
  constructor(fn) {
    this[PROMISE] = new Promise(fn)
      .then(tap(() => {
        this.fulfilled = true
        this.resolved = true
      }))
      .catch(x => {
        this.fulfilled = true
        this.rejected = true
        return Promise.reject(x)
      })
  }
  then(fn) {
    this[PROMISE].then(fn)
    return this
  }
  catch(fn) {
    this[PROMISE].catch(fn)
    return this
  }
  static resolve(x) {
    return new QueryablePromise((res) => res(x))
  }
  static reject(x) {
    return new QueryablePromise((_, rej) => rej(x))
  }
}

const resolvedPromise = new QueryablePromise((res) => {
  setTimeout(res, 200, 'resolvedPromise')
})

const rejectedPromise = new QueryablePromise((_, rej) => {
  setTimeout(rej, 200, 'rejectedPromise')
})

// ensure our promises have not been fulfilled
console.log('test 1 before: is resolved', resolvedPromise.resolved)
console.log('test 2 before: is rejected', rejectedPromise.rejected)


setTimeout(() => {
  // check to see the resolved status of our promise
  console.log('test 1 after: is resolved', resolvedPromise.resolved)
  console.log('test 2 after: is rejected', rejectedPromise.rejected)
}, 300)

// make sure we can immediately resolve a QueryablePromise
const immediatelyResolvedPromise = QueryablePromise.resolve('immediatelyResolvedPromise')
  // ensure we can chain then
  .then(trace('test 3 resolved'))
  .then(trace('test 3 resolved 2'))
  .catch(trace('test 3 rejected'))

// make sure we can immediately reject a QueryablePromise
const immediatelyRejectedPromise = QueryablePromise.reject('immediatelyRejectedPromise')
  .then(trace('test 4 resolved'))
  .catch(trace('test 4 rejected'))
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>

Upvotes: 1

0xaB
0xaB

Reputation: 471

enter image description here

promise-status-async does the trick. It is async but it does not use then to wait the promise to be resolved.

const {promiseStatus} = require('promise-status-async');
// ...
if (await promiseStatus(promise) === 'pending') {
    const idle = new Promise(function(resolve) {
        // can do some IDLE job meanwhile
    });
    return idle;
}

Upvotes: 42

Akrikos
Akrikos

Reputation: 3662

I found this solution to be simple and allow me to continue using native promises but add useful synchronous checks. I also didn't have to pull in an entire promise library.

CAVEAT: This only works if there is some sort of break in the current execution thread to allow the promises to execute BEFORE checking the synchronous constructs. That makes this of more limited usefulness than I'd initially thought -- still useful for my use case though (Thanks Benjamin Gruenbaum for pointing this out)

/**
 * This function allow you to modify a JS Promise by adding some status properties.
 * Based on: http://stackoverflow.com/questions/21485545/is-there-a-way-to-tell-if-an-es6-promise-is-fulfilled-rejected-resolved
 * But modified according to the specs of promises : https://promisesaplus.com/
 */
function MakeQuerablePromise(promise) {
    // Don't modify any promise that has been already modified.
    if (promise.isFulfilled) return promise;

    // Set initial state
    var isPending = true;
    var isRejected = false;
    var isFulfilled = false;

    // Observe the promise, saving the fulfillment in a closure scope.
    var result = promise.then(
        function(v) {
            isFulfilled = true;
            isPending = false;
            return v; 
        }, 
        function(e) {
            isRejected = true;
            isPending = false;
            throw e; 
        }
    );

    result.isFulfilled = function() { return isFulfilled; };
    result.isPending = function() { return isPending; };
    result.isRejected = function() { return isRejected; };
    return result;
}

wrappedPromise = MakeQueryablePromise(Promise.resolve(3)); 
setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1);

From https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved which based their answer on Is there a way to tell if an ES6 promise is fulfilled/rejected/resolved?

Upvotes: -1

Charles Bisbee
Charles Bisbee

Reputation: 391

This is older question but I was trying to do something similar. I need to keep n workers going. They are structured in a promise. I need to scan and see if they are resolved, rejected or still pending. If resolved, I need the value, if rejected do something to correct the issue or pending. If resolved or rejected I need to start another task to keep n going. I can't figure a way to do it with Promise.all or Promise.race as I keep working promises in an array and can find no way to delete them. So I create a worker that does the trick

I need a promise generator function that returns a promise which resolves or rejects as necessary. It is called by a function that sets up the framework to know what the promise is doing.

In the code below the generator simply returns a promise based on setTimeout.

Here it is

//argObj should be of form
// {succeed: <true or false, nTimer: <desired time out>}
function promiseGenerator(argsObj) {
  let succeed = argsObj.succeed;          
  let nTimer = argsObj.nTimer;
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (succeed) {
        resolve('ok');
      }
      else {
        reject(`fail`);
      }
    }, nTimer);
  })

}

function doWork(generatorargs) {
  let sp = { state: `pending`, value: ``, promise: "" };
  let p1 = promiseGenerator(generatorargs)
    .then((value) => {
      sp.state = "resolved";
      sp.value = value;
    })
    .catch((err) => {
      sp.state = "rejected";
      sp.value = err;
    })
  sp.promise = p1;
  return sp;
}

doWork returns an object containing the promise and the its state and returned value.

The following code runs a loop that tests the state and creates new workers to keep it at 3 running workers.

let promiseArray = [];

promiseArray.push(doWork({ succeed: true, nTimer: 1000 }));
promiseArray.push(doWork({ succeed: true, nTimer: 500 }));
promiseArray.push(doWork({ succeed: false, nTimer: 3000 }));

function loopTimerPromise(delay) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ok');
    }, delay)
  })
}

async function looper() {
  let nPromises = 3;      //just for breaking loop
  let nloop = 0;          //just for breaking loop
  let i;
  //let continueLoop = true;
  while (true) {
    await loopTimerPromise(900);  //execute loop every 900ms
    nloop++;
    //console.log(`promiseArray.length = ${promiseArray.length}`);
    for (i = promiseArray.length; i--; i > -1) {
      console.log(`index ${i} state: ${promiseArray[i].state}`);
      switch (promiseArray[i].state) {
        case "pending":
          break;
        case "resolved":
          nPromises++;
          promiseArray.splice(i, 1);
          promiseArray.push(doWork({ succeed: true, nTimer: 1000 }));
          break;
        case "rejected":
          //take recovery action
          nPromises++;
          promiseArray.splice(i, 1);
          promiseArray.push(doWork({ succeed: false, nTimer: 500 }));
          break;
        default:
          console.log(`error bad state in i=${i} state:${promiseArray[i].state} `)
          break;
      }
    }
    console.log(``);
    if (nloop > 10 || nPromises > 10) {
      //should do a Promise.all on remaining promises to clean them up but not for test
      break;
    }
  }
}

looper();

Tested in node.js

BTW Not in this answer so much but in others on similar topics, I HATE it when someone says "you don't understand" or "that's not how it works" I generally assume the questioner knows what they want. Suggesting a better way is great. A patient explanation of how promises work would also be good.

Upvotes: 0

Daniel Winterstein
Daniel Winterstein

Reputation: 2556

I've written a little npm package, promise-value, which provides a promise wrapper with a resolved flag:

https://www.npmjs.com/package/promise-value

It also gives synchronous access to the promise value (or error). This doesn't alter the Promise object itself, following the wrap rather than extend pattern.

Upvotes: 0

rabbitco
rabbitco

Reputation: 2850

You can use an (ugly) hack in Node.js until a native method is offered:

util = require('util');

var promise1 = new Promise (function (resolve) {
}

var promise2 = new Promise (function (resolve) {

    resolve ('foo');
}

state1 = util.inspect (promise1);
state2 = util.inspect (promise2);

if (state1 === 'Promise { <pending> }') {

    console.log('pending'); // pending
}

if (state2 === "Promise { 'foo' }") {

    console.log ('foo') // foo
}

Upvotes: 10

Moshe
Moshe

Reputation: 51

You can add a method to Promise.prototype. It looks like this:

Edited: The first solution is not working properly, like most of the answers here. It returns "pending" until the asynchronous function ".then" is invoked, which is not happen immediately. (The same is about solutions using Promise.race). My second solution solves this problem.

if (window.Promise) {
    Promise.prototype.getState = function () {
        if (!this.state) {
            this.state = "pending";
            var that = this;
            this.then(
                function (v) {
                    that.state = "resolved";
                    return v;
                },
                function (e) {
                    that.state = "rejected";
                    return e;
                });
        }
        return this.state;
    };
}

You can use it on any Promise. For exemple:

myPromise = new Promise(myFunction);
console.log(myPromise.getState()); // pending|resolved|rejected

Second (and correct) solution:

if (window.Promise) {
    Promise.stateable = function (func) {
        var state = "pending";
        var pending = true;
        var newPromise = new Promise(wrapper);
        newPromise.state = state;
        return newPromise;
        function wrapper(resolve, reject) {
            func(res, rej);
            function res(e) {
                resolve(e);
                if (pending) {
                    if (newPromise)
                        newPromise.state = "resolved";
                    else
                        state = "resolved";
                    pending = false;
                }
            }
            function rej(e) {
                reject(e);
                if (pending) {
                    if (newPromise)
                        newPromise.state = "rejected";
                    else
                        state = "rejected";
                    pending = false;
                }
            }
        }
    };
}

And use it:

Notice: In this solution you doesn't have to use the "new" operator.

myPromise = Promise.stateable(myFunction);
console.log(myPromise.state); // pending|resolved|rejected

Upvotes: 5

jib
jib

Reputation: 42500

Nope, no sync API, but here's my version of the async promiseState (with help from @Matthijs):

function promiseState(p) {
  const t = {};
  return Promise.race([p, t])
    .then(v => (v === t)? "pending" : "fulfilled", () => "rejected");
}

var a = Promise.resolve();
var b = Promise.reject();
var c = new Promise(() => {});

promiseState(a).then(state => console.log(state)); // fulfilled
promiseState(b).then(state => console.log(state)); // rejected
promiseState(c).then(state => console.log(state)); // pending

Upvotes: 123

Joshua Wise
Joshua Wise

Reputation: 663

As of Node.js version 8, you can now use the wise-inspection package to synchronously inspect native promises (without any dangerous hacks).

Upvotes: 2

Matthijs
Matthijs

Reputation: 867

It's indeed quite annoying that this basic functionality is missing. If you're using node.js then I know of two workarounds, neither of 'em very pretty. Both snippets below implement the same API:

> Promise.getInfo( 42 )                         // not a promise
{ status: 'fulfilled', value: 42 }
> Promise.getInfo( Promise.resolve(42) )        // fulfilled
{ status: 'fulfilled', value: 42 }
> Promise.getInfo( Promise.reject(42) )         // rejected
{ status: 'rejected', value: 42 }
> Promise.getInfo( p = new Promise(() => {}) )  // unresolved
{ status: 'pending' }
> Promise.getInfo( Promise.resolve(p) )         // resolved but pending
{ status: 'pending' }

There doesn't seem to be any way to distinguish the last two promise states using either trick.

1. Use the V8 debug API

This is the same trick that util.inspect uses.

const Debug = require('vm').runInDebugContext('Debug');

Promise.getInfo = function( arg ) {
    let mirror = Debug.MakeMirror( arg, true );
    if( ! mirror.isPromise() )
        return { status: 'fulfilled', value: arg };
    let status = mirror.status();
    if( status === 'pending' )
        return { status };
    if( status === 'resolved' )  // fix terminology fuck-up
        status = 'fulfilled';
    let value = mirror.promiseValue().value();
    return { status, value };
};

2. Synchronously run microtasks

This avoids the debug API, but has some frightening semantics by causing all pending microtasks and process.nextTick callbacks to be run synchronously. It also has the side-effect of preventing the "unhandled promise rejection" error from ever being triggered for the inspected promise.

Promise.getInfo = function( arg ) {
    const pending = {};
    let status, value;
    Promise.race([ arg, pending ]).then(
        x => { status = 'fulfilled'; value = x; },
        x => { status = 'rejected'; value = x; }
    );
    process._tickCallback();  // run microtasks right now
    if( value === pending )
        return { status: 'pending' };
    return { status, value };
};

Upvotes: 8

Ezequiel S. Pereira
Ezequiel S. Pereira

Reputation: 1

If you're using ES7 experimental you can use async to easily wrap the promise you want to listen.

async function getClient() {
  let client, resolved = false;
  try {
    client = await new Promise((resolve, reject) => {
      let client = new Client();

      let timer = setTimeout(() => {
         reject(new Error(`timeout`, 1000));
         client.close();
      });

      client.on('ready', () => {
        if(!resolved) {
          clearTimeout(timer);
          resolve(client);
        }
      });

      client.on('error', (error) => {
        if(!resolved) {
          clearTimeout(timer);
          reject(error);
        }
      });

      client.on('close', (hadError) => {
        if(!resolved && !hadError) {
          clearTimeout(timer);
          reject(new Error("close"));
        }
      });
    });

    resolved = true;
  } catch(error) {
    resolved = true;
    throw error;
  }
  return client;
}

Upvotes: 0

BigName
BigName

Reputation: 1097

what you can do, is to use a variable to store the state, manually set the state to that variable, and check that variable.

var state = 'pending';

new Promise(function(ff, rjc) {
  //do something async

  if () {//if success
    state = 'resolved';

    ff();//
  } else {
    state = 'rejected';

    rjc();
  }
});

console.log(state);//check the state somewhere else in the code

of course, this means you must have access to the original code of the promise. If you don't, then you can do:

var state = 'pending';

//you can't access somePromise's code
somePromise.then(function(){
  state = 'resolved';
}, function() {
  state = 'rejected';
})

console.log(state);//check the promise's state somewhere else in the code

My solution is more coding, but I think you probably wouldn't have to do this for every promise you use.

Upvotes: 3

Steween
Steween

Reputation: 374

You can make a race with Promise.resolve
It's not synchronous but happens now

function promiseState(p, isPending, isResolved, isRejected) {
  Promise.race([p, Promise.resolve('a value that p should not return')]).then(function(value) {
    if (value == 'a value that p should not return') {
      (typeof(isPending) === 'function') && isPending();
    }else {
      (typeof(isResolved) === 'function') && isResolved(value);
    }
  }, function(reason) {
    (typeof(isRejected) === 'function') && isRejected(reason);
  });
}

A little script for testing and understand their meaning of asynchronously

var startTime = Date.now() - 100000;//padding trick "100001".slice(1) => 00001
function log(msg) {
  console.log((""+(Date.now() - startTime)).slice(1) + ' ' + msg);
  return msg;//for chaining promises
};

function prefix(pref) { return function (value) { log(pref + value); return value; };}

function delay(ms) {
  return function (value) {
    var startTime = Date.now();
    while(Date.now() - startTime < ms) {}
    return value;//for chaining promises
  };
}
setTimeout(log, 0,'timeOut 0 ms');
setTimeout(log, 100,'timeOut 100 ms');
setTimeout(log, 200,'timeOut 200 ms');

var p1 = Promise.resolve('One');
var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "Two"); });
var p3 = Promise.reject("Three");

p3.catch(delay(200)).then(delay(100)).then(prefix('delayed L3 : '));

promiseState(p1, prefix('p1 Is Pending '), prefix('p1 Is Resolved '), prefix('p1 Is Rejected '));
promiseState(p2, prefix('p2 Is Pending '), prefix('p2 Is Resolved '), prefix('p2 Is Rejected '));
promiseState(p3, prefix('p3 Is Pending '), prefix('p3 Is Resolved '), prefix('p3 Is Rejected '));

p1.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
p2.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
p3.catch(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
log('end of promises');
delay(100)();
log('end of script');

results with delay(0) (comment the while in delay)

00001 end of promises
00001 end of script
00001 Level 1 : One
00001 Level 1 : Three
00001 p1 Is Resolved One
00001 p2 Is Pending undefined
00001 p3 Is Rejected Three
00001 Level 2 : One
00001 Level 2 : Three
00001 delayed L3 : Three
00002 Level 3 : One
00002 Level 3 : Three
00006 timeOut 0 ms
00100 timeOut 100 ms
00100 Level 1 : Two
00100 Level 2 : Two
00101 Level 3 : Two
00189 timeOut 200 ms

and the results of this test with firefox(chrome keep the order)

00000 end of promises
00100 end of script
00300 Level 1 : One
00300 Level 1 : Three
00400 p1 Is Resolved One
00400 p2 Is Pending undefined
00400 p3 Is Rejected Three
00400 Level 2 : One
00400 Level 2 : Three
00400 delayed L3 : Three
00400 Level 3 : One
00400 Level 3 : Three
00406 timeOut 0 ms
00406 timeOut 100 ms
00406 timeOut 200 ms
00406 Level 1 : Two
00407 Level 2 : Two
00407 Level 3 : Two

promiseState make .race and .then : Level 2

Upvotes: 21

Related Questions