8

Is there a simple and elegant way to determinate if variable is promise or object in javascript (es6)?

I have a variable

let payment;

and at one time in app, it can be either promise or object with real values. I know I can test it it contains some properties like

if (payment.amount)

but this doesn't seems like an elegant solution to me, because properties (data structure) can change in time. Till now I was using

if (typeof payment === 'object')

but I've just figured out, that promises are basically also objects. So this condition is always true.

Is it possible to tell if it's a promise or object with real data?

Patrik Šimunič
  • 449
  • 1
  • 4
  • 16
  • 8
    *"it can be either promise or object"* - that sounds like a design problem that you should fix. Make it consistent instead. – jonrsharpe Mar 26 '17 at 20:14
  • 2
    The 'duck-typing' approach is to check if it has `.then` property of type `function`. P.S. I agree with @jonrsharpe's design problem comment. – barry-johnson Mar 26 '17 at 20:14
  • 1
    I also agree it's a design problem, but not of my app - it's a design problem of Alt.js library, which returns either local stored variable from store or promise when fetching variable remotely from server. It'd cause more problems fix that, than test if is promise or object. – Patrik Šimunič Mar 26 '17 at 20:20
  • 2
    Wrap the result of the Alt.js function with `Promise.resolve()` and you should be good to go – barry-johnson Mar 26 '17 at 20:24
  • @barry-johanson That would be possible solution. But inside Alt.js function local() I have to return either real value from store or null, so it know the variable needs to be fetched remotely. In other words the function would return either promise or null - design problem remains, just in other place. – Patrik Šimunič Mar 26 '17 at 20:30

6 Answers6

23

Don't try to find out. Just make it always a promise.

payment = Promise.resolve(payment);

Promise.resolve creates a new promise object. If the value passed is itself a promise, the is resolved or rejected when the original one is. If it is anything else, the new promise is resolved immediately with that value.

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
15

As with all other native objects, you can use instanceof with the Promise constructor:

if (payment instanceof Promise)

However, if you don't know whether it's a native promise or from a custom library, you will usually want to detect a thenable by checking whether it has a .then method.

But you never really need to distinguish them (other than for debugging purposes). If there is a chance that something is a promise, you will always need to wait for, and construct a promise from it anyway. You don't need to do the distinction yourself, you can just use Promise.resolve to cast either promise, thenable or plain object to a promise of your favoured implementation - that's how Promise interoperability was designed in the first place, so it will absolutely work.

const promise = Promise.resolve(payment).then(value => {
    // value: always a plain value
}); // promise: always a promise
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
8

Check if it has a property then that is a function:

if(payment != null && typeof payment.then === 'function')
  // is a promise
else
  // is not a promise

As far as I know, this is the only proper way to check if something is a promise (or a "promise-like" thenable), taking into account all different possible promise implementations.

Frxstrem
  • 38,761
  • 9
  • 79
  • 119
  • Note that (at least for native Promises) `Object.prototype.toString.call(p)` will yield `[object Promise]` for any promise `p`. – Jared Smith Mar 27 '17 at 20:38
0

In case you are worried about using await on a plain-old function (rather than a Promise), it's actually okay to do that in ES6. The await gets ignored - no errors are thrown.

This may resolve your need to actually perform the comparison at all (as it did in my case when I first found this question)

1owk3y
  • 1,115
  • 1
  • 15
  • 30
-1
payment instanceof Promise

or this:

payment.constructor.toString().indexOf('Promise') > -1
The Moisrex
  • 1,857
  • 1
  • 14
  • 16
  • The second option will return `true` for any objects whose constructors simply reference the word `Promise`, which produces a lot of false positives. – gyre Mar 26 '17 at 20:36
-1
payment.constructor === Promise
Diego ZoracKy
  • 2,227
  • 15
  • 14