How to check if a variable is a promise in js?
To check if a variable is a Promise in JavaScript, you can use a simple and reliable check like this:
javascriptfunction isPromise(value) { return Boolean(value && typeof value.then === 'function' && typeof value.catch === 'function'); }
value &&
makes sure the value is not
null
or
undefined
.
typeof value.then === 'function'
checks if it has a
.then()
method.
typeof value.catch === 'function'
ensures it's not just a "thenable" (an object with
then
)
but a proper Promise with both
.then()
and
.catch()
.
javascriptconst p = new Promise((resolve) => resolve('done')); const notPromise = { then: () => {} }; console.log(isPromise(p)); // true console.log(isPromise(notPromise)); // false console.log(isPromise(null)); // false console.log(isPromise({})); // false
Let me know if you want to distinguish between native promises and "thenables" too.
Great question! Using
instanceof Promise
can work, but it has some limitations you should be aware of:
instanceof Promise
:
javascriptvalue instanceof Promise
This checks whether
value
was
created using the
Promise
constructor
from the same JavaScript realm
(same global context).
instanceof Promise
:
Cross-Realm Issues:
If a Promise comes from another frame or
window (e.g., in iframes or web workers),
instanceof Promise
will return
false
, even if it
is a
valid Promise.
javascript// In browser: const iframePromise = iframe.contentWindow.Promise.resolve(); console.log(iframePromise instanceof Promise); // false
Thenables (non-Promise objects with a .then):
instanceof Promise
does
not
detect "thenables" β objects that follow the
Promise API but aren't instances of the native
Promise
class.
typeof value.then === 'function'
is often preferred:
It works with native promises, thenables, and cross-realm promises.
Itβs more flexible for environments like testing, mocking, or interoperating with libraries that return thenables.
Use
instanceof Promise
if you're
only dealing with native Promises in the same
environment.
Use the
.then/.catch
check if you want to be
robust, cross-compatible, and cover thenables
too.
Want to see a utility that handles both?
Sure! A
thenable
is any object that implements a
.then()
method, even if it's not a real instance of
Promise
.
Here's a simple example:
javascriptconst thenable = { then: function (resolve, reject) { setTimeout(() => resolve('Resolved from thenable!'), 1000); } };
You can use this
thenable
just like a Promise in some contexts:
javascriptPromise.resolve(thenable).then((value) => { console.log(value); // π "Resolved from thenable!" after 1 second });
They allow you to create "promise-like" behavior
without using the native
Promise
constructor. Many libraries (e.g., jQuery's old
Deferred objects or custom async utilities) used this
pattern before native Promises became widespread.
Let me know if you want to build your own mini Promise using thenablesβitβs a fun exercise!
Great question β this comes up a lot in async JavaScript.
await
something that's
not a Promise?
It still works β but maybe not how you'd expect.
javascriptasync function test() { const value = await 42; console.log(value); // π 42 } test();
await
converts any value into a resolved
Promise
behind the scenes using
Promise.resolve(value)
.
So
await 42
is the same as
await Promise.resolve(42)
.
This means you can
await
:
Primitives like numbers, strings β returns them immediately.
Plain objects β also returned immediately.
Thenables
β
await
will follow their
.then()
behavior.
Real Promises
β
await
waits for them to resolve or reject.
javascriptasync function test() { const value = await (() => { throw new Error("Oops"); })(); }
If the expression throws an error immediately, it will be caught like any other synchronous error.
But if it returns a rejected Promise (or a
thenable that rejects), the
await
will throw that rejection.
Value passed to
await
|
Behavior |
---|---|
Promise.resolve(1)
|
waits, then returns
1
|
42
|
immediately returns
42
|
"hello"
|
immediately returns
"hello"
|
{}
|
immediately returns the object |
{ then: fn }
(thenable)
|
calls
.then()
and follows logic
|
throw new Error()
|
throws immediately |
So yeah β
await
is surprisingly flexible!
Want to test this behavior in code together?
Absolutely! Here's a
TypeScript-compatible
version of the
isPromise
function, with proper type guards:
tsfunction isPromise<T = unknown>(value: any): value is Promise<T> { return ( value !== null && typeof value === 'object' && typeof value.then === 'function' && typeof value.catch === 'function' ); }
value is Promise<T>
is a
TypeScript type guard
β it tells the compiler "if this returns true, you
can safely treat
value
as a
Promise<T>
."
value: any
keeps the function flexible (you can pass anything
in).
You can optionally specify the
T
type for stronger inference (e.g.,
isPromise<string>(someValue)
).
tsconst maybePromise: unknown = fetch('https://api.example.com'); if (isPromise<Response>(maybePromise)) { maybePromise.then(res => console.log(res.status)); } else { console.log('Not a promise!'); }
Let me know if you want a stricter version or one that also detects thenables!