66

I have this situation in which I would like to know what the status is of a promise. Below, the function start only calls someTest if it is not running anymore (Promise is not pending). The start function can be called many times, but if its called while the tests are still running, its not going to wait and returns just false

class RunTest {
    start() {
         retVal = false;

         if (!this.promise) {
             this.promise = this.someTest();
             retVal = true;                
         }

         if ( /* if promise is resolved/rejected or not pending */ ) {
             this.promise = this.someTest();
             retVal = true;
         }

         return retVal;
    }

    someTest() {
        return new Promise((resolve, reject) => {
            // some tests go inhere
        });
    }
}

I cannot find a way to simply check the status of a promise. Something like this.promise.isPending would be nice :) Any help would be appreciated!

Amit
  • 45,440
  • 9
  • 78
  • 110
Jeanluca Scaljeri
  • 26,343
  • 56
  • 205
  • 333
  • 1
    What is the use case? I don't think native promises support this, it is a weird thing to want to do, but Blubird does http://bluebirdjs.com/docs/api/ispending.html – elclanrs Mar 29 '16 at 20:09
  • Not sure if you've checked Mozilla yet, but they have nice examples and documentation on promises - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise – Zack Macomber Mar 29 '16 at 20:11
  • In my case its a `setInterval` calling something async all the time. And that something should only be running one at the time. I could of course set an variable on `this`, like `this.isBusy = true` for example. But in my opinion, that sounds like a work-around for not knowing the status of a promise – Jeanluca Scaljeri Mar 29 '16 at 20:15
  • The mozilla page is indeed great, but there is nothing about how to check the status of a promise. If I missed it, please let met know! – Jeanluca Scaljeri Mar 29 '16 at 20:17
  • You could use [this](http://stackoverflow.com/a/35820220/918910) since you're using `setInterval` anyway. – jib Mar 29 '16 at 21:52
  • 23
    Checking whether a promise is pending doesn't seem "weird", it seems absolutely fundamental. – jchook Jul 18 '17 at 02:43
  • 4
    I wonder if the people marking this as a duplicate realize, this is a pure javascript question and the duplicate is a node.js question. One of the votes to mark it as duplicate even mention a node library which does not apply in this case. Pity. – netskink Jan 27 '22 at 03:48

2 Answers2

35

You can attach a then handler that sets a done flag on the promise (or the RunTest instance if you prefer), and test that:

     if (!this.promise) {
         this.promise = this.someTest();
         this.promise.finally(() => { this.promise.done = true; });
         retVal = true;                
     }

     if ( this.promise.done ) {
         this.promise = this.someTest();
         this.promise.finally(() => { this.promise.done = true; });
         retVal = true;
     }

The finally() handler ensures that the done flag is set regardless of the outcome of the promise.

You probably want to wrap that in a function though to keep the code DRY.

Daniel
  • 1,695
  • 15
  • 33
Amit
  • 45,440
  • 9
  • 78
  • 110
  • 1
    I'd guess it would be easier to just do `this.promise = null` – Bergi Mar 29 '16 at 21:10
  • 1
    An "empty" `catch` argument doesn't work. You still need to explicitly pass an empty function, or it is just meaningless. – Bergi Mar 29 '16 at 21:11
  • 1
    @Bergi - good point about the empty catch, correcting now... Regarding `this.promise = null`, it's OK for this specific case, but not as robust in general. For example, of the promise is exposed out of the function, having the property available on the promise is nicer. – Amit Mar 29 '16 at 21:14
  • Sure, but usually nobody expects to use such a property on a promise, so there's no need to expose it. In all cases where you think you'd need to determine the state of the promise, what you actually need is checking whether your callback was already called or not. – Bergi Mar 29 '16 at 21:18
  • 10
    FWIW, replacing `.catch(() => {}).then()` with [`.finally()`](https://caniuse.com/#feat=promise-finally) might be considered instead. – Thomas Urban Mar 01 '20 at 11:06
2
class RunTest {
   constructor() {
    this.isRunning = false;
   }
   start() {
      console.log('isrunning', this.isRunning);
      var retVal = false;
      if(!this.isRunning) {
        this.promise = this.someTest();
        this.promise.catch().then(() => { this.isRunning = false; });
        retVal = true;                
      }
      return retVal;
    }
    someTest() {
        this.isRunning = true;
        return new Promise((resolve, reject) => {
          setTimeout(function() {
             //some tests go inhere
             resolve();
           }, 1000);
        });
    }
};

var x = new RunTest();

x.start(); //logs false
x.start(); //logs true

setTimeout(function() {
    //wait for a bit
  x.start(); //logs false
}, 2000);
Daniel Graham
  • 399
  • 3
  • 13
  • 1
    There's a downside to returning the promise after the `then ()` - you can't have a resolved value passed on to the next handler. Also, this won't work if the tests throw (see my answer about this). – Amit Mar 29 '16 at 20:22
  • 1
    The `then` callback [has lost the methods context](http://stackoverflow.com/q/20279484/1048572) – Bergi Mar 29 '16 at 21:13
  • https://jsfiddle.net/tchalvakspam/wszyqj3t/3/ – Kzqai Dec 22 '17 at 15:48
  • @Amit Changing to `.then(resp => { this.isRunning = false; return resp; })` resolves this issue easily enough; however, replacing `.catch().then()` with `.finally()` is a much better solution – Daniel May 17 '23 at 17:27