How to Check if a Value is a Promise in JavaScript
In modern asynchronous JavaScript, it's common to work with functions that might return a direct value or a Promise that will resolve to a value. To handle both cases uniformly, you need a reliable way to determine if a given value is a "thenable"—an object that behaves like a Promise (i.e., has a .then() method).
This guide will explain why you often don't need a strict "is this a promise?" check. Instead, it will teach you the modern, idiomatic solution for handling any value as if it were a promise by using Promise.resolve(). We will also cover the classic "duck typing" method for when you need to explicitly check a value's shape.
The Core Task: Handling a Value That Might Be a Promise
Often, the goal isn't just to ask "is this a promise?" but rather to ensure you can use .then() on a value, regardless of whether it's a Promise or a direct value.
Example of code with problems:
function processData(data) {
// If `data` is the number 42, this will fail.
// ⛔️ Uncaught TypeError: data.then is not a function
data.then(result => {
console.log(result);
});
}
// This call works
processData(Promise.resolve(42));
// This call fails
processData(42);
The Modern Solution (Recommended): Promise.resolve()
The Promise.resolve() method is the perfect tool for this. It is designed to "wrap" a value in a Promise.
The Logic
- If you pass a
PromisetoPromise.resolve(), it returns that samePromise. - If you pass a non-Promise value (like a number or a string), it returns a new
Promisethat immediately resolves with that value.
This means the return value of Promise.resolve() is always a Promise, so you can safely call .then() on it.
Solution:
function processData(data) {
// ✅ Wrap the input in Promise.resolve() to guarantee it's a Promise
Promise.resolve(data).then(result => {
console.log(result);
});
}
// This call now works
processData(Promise.resolve(42)); // Output: 42
// This call also works
processData(42); // Output: 42
This is the idiomatic, modern, and recommended way to handle values that might be "thenable."
The Classic Method: "Duck Typing" to Check for .then()
If you truly need to check if a value is like a Promise and return a boolean, you can use a technique called "duck typing." The principle is: "If it walks like a duck and it quacks like a duck, then it must be a duck." In our case, if a value is an object and has a .then() method, we can consider it "thenable" (promise-like).
Solution:
function isThenable(value) {
return value !== null &&
typeof value === 'object' &&
typeof value.then === 'function';
}
const myPromise = new Promise(resolve => resolve());
const myObject = { id: 1 };
const myNumber = 42;
console.log(isThenable(myPromise)); // Output: true
console.log(isThenable(myObject)); // Output: false
console.log(isThenable(myNumber)); // Output: false
// Be aware of the limitation:
const fakePromise = { then: () => {} };
console.log(isThenable(fakePromise)); // Output: true
How it Works:
value !== null: This is a crucial first check becausetypeof nullis paradoxically'object'.typeof value === 'object': Ensures the value is an object.typeof value.then === 'function': Checks if it has a.thenproperty that is a function.
How the Methods Work
Promise.resolve(): This is a static method of thePromiseclass that is part of the ECMAScript standard. It is designed specifically for standardizing values into a promise-based workflow.- Duck Typing: This is a logical check based on an object's properties rather than its specific type or class. It is a common pattern in dynamically-typed languages like JavaScript. Its main weakness is that it can be fooled by an object that coincidentally has the same method names as a
Promise.
Conclusion
When dealing with values that may or may not be a Promise, it's important to choose the right tool for the job.
The key takeaways are:
- If your goal is to handle a value in a promise-based workflow, the
Promise.resolve(value)method is the recommended best practice. It safely converts any value into aPromise, allowing you to use.then()on it without needing to check its type first. - If your goal is to strictly check if a value has the shape of a
Promiseand return a boolean, you can use "duck typing" to check if the value is an object with a.then()method.
For most modern asynchronous code, the Promise.resolve() pattern is the cleaner and more idiomatic solution.