no-promise-executor-return
The executor function passed to new Promise should not return a value. The return value is ignored and returning a value is usually a mistake.
Rule Type: Problem
Fixable: No
Why This Rule Exists
The executor function of a Promise should call resolve() or reject() to settle the promise. The return value of the executor is ignored, so returning a value is pointless and usually indicates a misunderstanding of how Promises work.
new Promise(function executor(resolve, reject) {
readFile('foo.txt', function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
Rule Details
This rule disallows returning values from Promise executor functions. Only return without a value (for control flow) is allowed.
Examples
Incorrect Code
// Returning a default value
new Promise((resolve, reject) => {
if (someCondition) {
return defaultResult; // Error: this value is ignored
}
getSomething((err, result) => {
if (err) reject(err);
else resolve(result);
});
});
// Implicit return from arrow function
new Promise((resolve, reject) => getSomething((err, data) => {
if (err) reject(err);
else resolve(data);
})); // Error: getSomething's return value is ignored
// Explicit return value
new Promise(() => {
return 1; // Error
});
// Arrow function implicit return
new Promise(r => r(1)); // Error: returns result of r(1)
Correct Code
// Use resolve instead of return
new Promise((resolve, reject) => {
if (someCondition) {
resolve(defaultResult); // Correct
return; // OK: empty return for control flow
}
getSomething((err, result) => {
if (err) reject(err);
else resolve(result);
});
});
// Add braces to prevent implicit return
new Promise((resolve, reject) => {
getSomething((err, data) => {
if (err) reject(err);
else resolve(data);
});
});
// Wrap body in braces
new Promise(r => { r(1); });
// Better: Use Promise.resolve for simple cases
Promise.resolve(1);
Common Patterns
Mixing Return with Resolve
Don’t confuse return with resolve(). They serve different purposes in Promise executors.
// Wrong: return doesn't settle the promise
new Promise((resolve, reject) => {
if (condition) {
return value; // Doesn't work!
}
});
// Right: resolve settles the promise
new Promise((resolve, reject) => {
if (condition) {
resolve(value);
return; // Optional: for control flow
}
});
Arrow Function Gotchas
// Wrong: Implicit return
new Promise(resolve => doAsyncWork(resolve));
// Right: Explicit function body
new Promise(resolve => {
doAsyncWork(resolve);
});
// Or use Promise wrapper
new Promise((resolve, reject) => {
doAsyncWork()
.then(resolve)
.catch(reject);
});
// Better: Avoid Promise constructor entirely
function myPromise() {
return doAsyncWork();
}
Early Exit Pattern
// Use empty return for early exit (this is OK)
new Promise((resolve, reject) => {
if (cacheHit) {
resolve(cachedValue);
return; // Stop execution
}
fetchFromServer((err, data) => {
if (err) reject(err);
else resolve(data);
});
});
Options
allowVoid
Type: boolean
Default: false
Allows returning void values (explicit void operator).
// With { "allowVoid": true }
// These are allowed:
new Promise((resolve, reject) => {
if (someCondition) {
return void resolve(defaultResult); // OK
}
});
new Promise((resolve, reject) => void getSomething((err, data) => {
if (err) reject(err);
else resolve(data);
})); // OK
new Promise(r => void r(1)); // OK
The allowVoid option is useful if you prefer the explicit void operator to make it clear you’re intentionally discarding a return value.
Configuration
// Disallow all returns with values
{
"rules": {
"no-promise-executor-return": "error"
}
}
// Allow void returns
{
"rules": {
"no-promise-executor-return": ["error", {
"allowVoid": true
}]
}
}
Best Practices
1. Call resolve/reject, don’t return
// Wrong
new Promise((resolve) => {
return value;
});
// Right
Promise.resolve(value);
// Unnecessary Promise constructor
new Promise(resolve => {
resolve(value);
});
// Simpler
Promise.resolve(value);
3. Avoid the Promise constructor when possible
// Unnecessary wrapping
function getUser(id) {
return new Promise((resolve, reject) => {
fetch(`/api/user/${id}`)
.then(resolve)
.catch(reject);
});
}
// Just return the promise directly
function getUser(id) {
return fetch(`/api/user/${id}`);
}
4. Use async/await instead
// Complex Promise constructor
function getData() {
return new Promise((resolve, reject) => {
fetchUser((err, user) => {
if (err) return reject(err);
fetchPosts(user.id, (err, posts) => {
if (err) return reject(err);
resolve({ user, posts });
});
});
});
}
// Cleaner with async/await
async function getData() {
const user = await fetchUserPromise();
const posts = await fetchPostsPromise(user.id);
return { user, posts };
}
When Not to Use It
This rule should almost never be disabled. If you’re returning values from Promise executors, you likely have a bug or misunderstanding.
Further Reading