Check if Promise JS

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:

javascript
function isPromise(value) { return Boolean(value && typeof value.then === 'function' && typeof value.catch === 'function'); }

βœ… Explanation:

  • 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().

πŸ’‘ Example usage:

javascript
const 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.

Why not just use instanceof Promise?

Great question! Using instanceof Promise can work, but it has some limitations you should be aware of:


βœ… instanceof Promise:

javascript
value instanceof Promise

This checks whether value was created using the Promise constructor from the same JavaScript realm (same global context).


❗ Drawbacks of instanceof Promise:

  1. 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
  2. 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.


βœ… Why 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.


πŸ“Œ TL;DR:

  • 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?

Give me an example of a thenable

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:

javascript
const thenable = { then: function (resolve, reject) { setTimeout(() => resolve('Resolved from thenable!'), 1000); } };

You can use this thenable just like a Promise in some contexts:

javascript
Promise.resolve(thenable).then((value) => { console.log(value); // πŸ‘‰ "Resolved from thenable!" after 1 second });

🧠 Why are thenables useful?

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!

What happens if I wait something that is not a promise?

Great question β€” this comes up a lot in async JavaScript.

πŸ€” What happens if you await something that's not a Promise?

It still works β€” but maybe not how you'd expect.

javascript
async function test() { const value = await 42; console.log(value); // πŸ‘‰ 42 } test();

βœ… How it works:

  • 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.


❗ What if the value throws?

javascript
async 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.


Summary:

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?

Give me a typescript compatible version of the isPromise func

Absolutely! Here's a TypeScript-compatible version of the isPromise function, with proper type guards:

ts
function isPromise<T = unknown>(value: any): value is Promise<T> { return ( value !== null && typeof value === 'object' && typeof value.then === 'function' && typeof value.catch === 'function' ); }

βœ… Explanation:

  • 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)).


πŸ§ͺ Example usage:

ts
const 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!