Whether it's an ES6 Promise or a bluebird Promise, Q Promise, etc.
How do I test to see if a given object is a Promise?
Whether it's an ES6 Promise or a bluebird Promise, Q Promise, etc.
How do I test to see if a given object is a Promise?
If it has a .then
function - that's the only standard promise libraries use.
The Promises/A+ specification has a notion called then
able which is basically "an object with a then
method". Promises will and should assimilate anything with a then method. All of the promise implementation you've mentioned do this.
If we look at the specification:
2.3.3.3 if
then
is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise
It also explains the rationale for this design decision:
This treatment of
then
ables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliantthen
method. It also allows Promises/A+ implementations to “assimilate” nonconformant implementations with reasonable then methods.
You shouldn't - instead call Promise.resolve(x)
(Q(x)
in Q) that will always convert any value or external then
able into a trusted promise. It is safer and easier than performing these checks yourself.
You can always run it through the test suite :D
Checking if something is promise unnecessarily complicates the code, just use Promise.resolve
Promise.resolve(valueOrPromiseItDoesntMatter).then(function(value) {
})
Here's my original answer, which has since been ratified in the spec as the way to test for a promise:
Promise.resolve(obj) == obj
This works because the algorithm explicitly demands that Promise.resolve
must return the exact object passed in if and only if it is a promise by the definition of the spec.
I have another answer here, which used to say this, but I changed it to something else when it didn't work with Safari at that time. That was a year ago, and this now works reliably even in Safari.
I would have edited my original answer, except that felt wrong, given that more people by now have voted for the altered solution in that answer than the original. I believe this is the better answer, and I hope you agree.
To see if the given object is a ES6 Promise, we can make use of this predicate:
function isPromise(p) {
return p && Object.prototype.toString.call(p) === "[object Promise]";
}
Call
ing toString
directly from the Object.prototype
returns a native string representation of the given object type which is "[object Promise]"
in our case. This ensures that the given object
toString
method of the given object.instanceof
or isPrototypeOf
.However, any particular host object, that has its tag modified via Symbol.toStringTag
, can return "[object Promise]"
. This may be the intended result or not depending on the project (e.g. if there is a custom Promise implementation).
To see if the object is from a native ES6 Promise, we can use:
function isNativePromise(p) {
return p && typeof p.constructor === "function"
&& Function.prototype.toString.call(p.constructor).replace(/\(.*\)/, "()")
=== Function.prototype.toString.call(/*native object*/Function)
.replace("Function", "Promise") // replacing Identifier
.replace(/\(.*\)/, "()"); // removing possible FormalParameterList
}
According to this and this section of the spec, the string representation of function should be:
"function Identifier ( FormalParameterListopt ) { FunctionBody }"
which is handled accordingly above. The FunctionBody is [native code]
in all major browsers.
MDN: Function.prototype.toString
This works across multiple environment contexts as well.
Not an answer to the full question but I think it's worth to mention that in Node.js 10 a new util function called isPromise
was added which checks if an object is a native Promise or not:
const utilTypes = require('util').types
const b_Promise = require('bluebird')
utilTypes.isPromise(Promise.resolve(5)) // true
utilTypes.isPromise(b_Promise.resolve(5)) // false
This is how graphql-js package detects promises:
function isPromise(value) {
return Boolean(value && typeof value.then === 'function');
}
value
is the returned value of your function. I'm using this code in my project and have no problem so far.
Here is the code form https://github.com/ssnau/xkit/blob/master/util/is-promise.js
!!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
if an object with a then
method, it should be treat as a Promise
.
If you are in an async method you can do this and avoid any ambiguity.
async myMethod(promiseOrNot){
const theValue = await promiseOrNot()
}
If the function returns promise, it will await and return with the resolved value. If the function returns a value, it will be treated as resolved.
If the function does not return a promise today, but tomorrow returns one or is declared async, you will be future-proof.
it('should return a promise', function() {
var result = testedFunctionThatReturnsPromise();
expect(result).toBeDefined();
// 3 slightly different ways of verifying a promise
expect(typeof result.then).toBe('function');
expect(result instanceof Promise).toBe(true);
expect(result).toBe(Promise.resolve(result));
});
after searching for a reliable way to detect Async functions or even Promises, i ended up using the following test :
() => fn.constructor.name === 'Promise' || fn.constructor.name === 'AsyncFunction'
ES6:
const promise = new Promise(resolve => resolve('olá'));
console.log(promise.toString().includes('Promise')); //true
Update: This is no longer the best answer. Please vote up my other answer instead.
obj instanceof Promise
should do it. Note that this may only work reliably with native es6 promises.
If you're using a shim, a promise library or anything else pretending to be promise-like, then it may be more appropriate to test for a "thenable" (anything with a .then
method), as shown in other answers here.
if (typeof thing.then === 'function') {
// probably a promise
} else {
// definitely not a promise
}
In case you are using Typescript, I'd like to add that you can use the "type predicate" feature. Just should wrap the logical verification in a function that returns x is Promise<any>
and you won't need to do typecasts. Below on my example, c
is either a promise or one of my types which I want to convert into a promise by calling the c.fetch()
method.
export function toPromise(c: Container<any> | Promise<any>): Promise<any> {
if (c == null) return Promise.resolve();
return isContainer(c) ? c.fetch() : c;
}
export function isContainer(val: Container<any> | Promise<any>): val is Container<any> {
return val && (<Container<any>>val).fetch !== undefined;
}
export function isPromise(val: Container<any> | Promise<any>): val is Promise<any> {
return val && (<Promise<any>>val).then !== undefined;
}
More info: https://www.typescriptlang.org/docs/handbook/advanced-types.html
I use this function as a universal solution:
function isPromise(value) {
return value && value.then && typeof value.then === 'function';
}