Get Started
- CodeAnt AI
- Control Center
- Pull Request Review
- IDE
- Compliance
- Anti-Patterns
- Code Governance
- Infrastructure Security Database
- Application Security Database
Promise
function getUser(id) {
return getUserFromDatabase(id).then(user => {
// Mistake: No return statement. This will lead to an undefined resolution of the promise.
console.log(user.name);
});
}
- This ESLint rule ensures that every promise chain is guaranteed to pass a value to the next handler by enforcing a
return
orthrow
statement insidethen()
orcatch()
callbacks. This prevents cases where a promise might resolve withundefined
unexpectedly, leading to difficult-to-debug issues in asynchronous code flows. - The rule aids in maintaining the consistency of promise handling by making explicit the expectation for each promise to contribute a meaningful value to the chain or to propagate an error. This is crucial in complex applications where the flow of asynchronous operations must be clear and predictable.
- By enforcing an explicit
return
orthrow
in promise handlers, the rule can help prevent subtle bugs that could arise from implicit returns in arrow functions or overlooked return statements. For example, a developer might unintentionally omit a return statement, causing the promise to resolve withundefined
, which can lead to runtime errors when the subsequent code expects a value. - The rule’s configurability, such as the
ignoreLastCallback
option, allows teams to tailor the rule to their specific project needs or coding styles, providing flexibility. For instance, a team could choose to ignore this rule for the last callback in a promise chain if their application logic or style guide deems it reasonable, ensuring the rule adds value without being overly prescriptive.
function checkData(data) {
return new Promise((resolve, reject) => {
if (data.isValid) {
resolve(data);
} else {
reject(new Error('Invalid data'));
}
});
}
- Encourages reusability of existing promises or functions that return a promise, thereby reducing code duplication and fostering a DRY (Don’t Repeat Yourself) principle. This is evident in transitioning from the
bad
tofix
code snippets wherecheckData
function is refactored to utilizeasyncValidateData
instead of creating a new promise. - Enhances readability and maintainability of the code by discouraging the inline creation of new Promises for operations that might already be encapsulated within existing functions. By prompting the reuse of
asyncValidateData
, developers are nudged towards leveraging abstractions that encapsulate the asynchronous logic. - Aims to minimize potential errors related to the incorrect handling of asynchronous operations by promoting the use of higher-level abstractions or utilities that are likely tested and properly handled. The initial creation of a new promise directly within
checkData
increases the risk of mishandling rejection or resolution states, which is mitigated when reusingasyncValidateData
. - Advocates for better architectural practices by encouraging the centralization of asynchronous data handling logic within dedicated functions (
asyncValidateData
) instead of scattering new Promise instantiations across the codebase. This facilitates easier updates and optimizations to the asynchronous logic, as changes need to be made in fewer places.
function fetchData() {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
// Missing .catch() or return statement.
}
- Ensures that promises are either caught with a
.catch()
handler or returned for error handling further up the call chain, promoting robust error handling practices in asynchronous code, which is crucial to avoid unhandled promise rejections that can lead to runtime errors and application instability. - Provides flexibility in error handling strategies by allowing configuration options such as
allowThen
for error handling in a second.then()
method parameter,allowFinally
for using.finally()
as a termination method, and specifying alternative termination methods. This accommodates various coding styles and requirements while still enforcing error management. - Helps in maintaining consistent promise handling patterns across the codebase, which can significantly improve code readability and maintainability. By encouraging developers to explicitly handle or return promises, the rule assists in making the control flow easier to understand, especially in complex applications with multiple asynchronous operations.
- By reporting where promises are not properly handled or returned, it aids in proactive debugging and can prevent potential issues from escalating into critical failures. This preemptive action saves developers time and effort in troubleshooting and reinforces the importance of considering error-handling paths during the initial development process, leading to more reliable software.
const fs = require('fs');
function readFileAsArray(fileName, cb) {
return new Promise((resolve, reject) => {
fs.readFile(fileName, function(err, data) {
if (err) {
reject(err);
cb(err);
return;
}
const lines = data.toString().trim().split('\n');
resolve(lines);
cb(null, lines);
});
});
}
- This rule helps prevent the common mistake of mixing callbacks with promises, which can lead to confusing and hard-to-maintain code. In the ‘bad’ example, a callback and a promise are used together, creating a situation where error handling and flow control become more complex than necessary.
- It enforces a cleaner, more modern approach to asynchronous JavaScript by encouraging the use of promises or async/await syntax over callbacks. This aligns with the evolution of JavaScript towards promises for asynchronous operations, as seen in the ‘fix’ snippet where the promise-based ‘fs’ module is used.
- By discouraging the use of callbacks within promises, it avoids potential issues related to error handling. In the ‘bad’ example, an error is both rejected and passed to a callback, which could lead to the same error being handled multiple times or in different ways, potentially leading to inconsistencies.
- The rule aids in preventing the anti-pattern where a promise is returned (suggesting that it should be used for flow control) but a callback is also executed within the promise executor. This can lead to confusion about which mechanism (the promise or the callback) should be used by the caller to handle completion or errors, as seen in the ‘bad’ example where both a promise and a callback are used to manage the same operation.
new Promise((resolve, reject) => {
doSomething()
.then(result => {
if (result.isSuccess) {
resolve('Success');
} else {
reject('Failure');
resolve('Trying to resolve after reject'); // This line is problematic
}
});
});
- The rule specifically aims to prevent the anti-pattern of resolving or rejecting a promise more than once within the same promise construction callback. This is important because resolving or rejecting a promise multiple times can lead to unpredictable behaviors and bugs that are hard to trace in asynchronous code.
- By tracking the resolution paths within a promise and reporting when a promise is potentially or certainly resolved multiple times, this rule helps enforce best practices for promise handling, ensuring that each promise is only settled (either resolved or rejected) once, which aligns with the specification and expected behavior of promises in JavaScript.
- The rule aids in maintaining code clarity in complex asynchronous operations. Given the example of incorrect code, where a promise is attempted to be resolved after it has been rejected, applying this rule would catch such misuse, enhancing the readability and maintainability of the code by ensuring that promises are used as intended.
- It encourages the development of more robust error handling in asynchronous functions. By catching instances where a promise might be resolved after being rejected (or vice versa), it pushes developers towards patterns that properly utilize catch blocks or chain rejections, leading to cleaner and more fault-tolerant code.
const getUserData = (userId) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simulating a fetch operation
if (userId === 123) {
resolve({ name: 'John Doe', age: 30 });
} else {
reject(new Error('User not found'));
}
}, 1000);
});
}
getUserData(123)
.then(userData => console.log(userData))
.catch(err => console.error(err));
- Ensures consistency in handling asynchronous operations by discouraging the direct use of the native
Promise
constructor when there could be simpler or more advanced abstractions available which could lead to clearer, more maintainable code. - Helps in promoting the use of modern JavaScript features like
async/await
that can make asynchronous code easier to read and debug, especially in complex applications where understanding the flow of asynchronous operations is vital. - Aids in reducing potential errors related to the improper handling of
Promise
states (pending, fulfilled, rejected) by encouraging patterns that might aid in avoiding common pitfalls such as unhandled promise rejections. - Encourages the exploration and adoption of utility functions or libraries that can abstract common asynchronous patterns, potentially leading to code that is less error-prone and adheres to the DRY (Don’t Repeat Yourself) principle.
getUserById(1).then(user => {
getPermissions(user).then(permissions => {
// Operations with permissions
console.log(permissions);
});
});
- This rule specifically targets the common anti-pattern of nesting promises, which can lead to code that is harder to read and maintain. By enforcing a flat structure for promise chains, it ensures that asynchronous operations are handled in a more streamlined and understandable way.
- The rule intelligently allows for exceptions where variables from a parent scope are used within nested promises, acknowledging the situation where flattening the promise chain wouldn’t be straightforward due to the dependencies between promises. This nuanced approach prevents developers from refactoring code to a pattern that would break the logic or make variable scope handling more complicated.
- By providing a clear example of bad and fixed code, this rule not only identifies where the issues lie but also guides developers towards a cleaner, more maintainable way to handle asynchronous logic using promises. This educative aspect helps in spreading best practices within the team.
- It leverages the ESLint ecosystem to seamlessly integrate into the development workflow, allowing for real-time feedback during coding sessions. This immediate reinforcement helps in gradually improving the codebase with minimal disruption and encourages developers to adopt better coding habits over time.
// Bad code: Incorrectly using 'new' with Promise.resolve
const promise = new Promise.resolve('success');
-
The rule specifically targets the incorrect use of
new
withPromise
static methods such asPromise.resolve()
,Promise.reject()
,Promise.all()
, andPromise.race()
. These static methods are meant to be called directly on thePromise
constructor without creating a new instance. -
It helps in preventing runtime errors that can occur from incorrectly instantiating
Promise
static methods withnew
. Since these methods are not constructor functions and are not meant to be called withnew
, doing so can lead to unexpected behavior or errors in the code. -
It enhances code readability and maintainability by ensuring that
Promise
static methods are used correctly. Usingnew
with these methods is a common mistake among developers, especially those new to JavaScript’sPromise
API, and enforcing this rule can help catch such mistakes early. -
The rule provides an automatic fix to incorrect usage by removing the
new
keyword when applied toPromise
static methods. This not only helps in correcting the errors but also educates developers about the proper usage ofPromise
static methods, promoting best practices within the codebase.
// Assume this function is used as a callback somewhere in the code
fs.readFile('/path/to/file', (err, data) => {
if (err) {
console.error(err);
return;
}
axios.get('https://api.example.com/data').then(response => {
console.log(response.data);
}).catch(error => {
console.error(error);
});
});
-
The rule helps maintain asynchronous code consistency by discouraging the mixing of callback patterns with promises within the same function, which prevents potential confusion regarding how error handling is managed and how the flow of asynchronous operations is understood. Mixing patterns can lead to harder-to-follow code and possible errors in handling asynchronous operations.
-
It encourages the use of modern JavaScript async/await syntax or thenable chains at a higher level rather than nesting promises inside callbacks. This leads to cleaner, more readable code, which is easier to debug and maintain.
-
Enforcing this rule helps in identifying and refactoring legacy code that uses mixed asynchronous patterns, promoting the adoption of newer, more efficient patterns of handling asynchronous operations. Such refactoring can lead to better performance and more consistent error handling.
-
It assists in avoiding potential issues related to the misuse of promises within callbacks, such as unhandled promise rejections or unexpected behavior due to the mixing of different asynchronous control flows, which can be hard to debug and lead to runtime errors.
function fetchData() {
let data;
return fetch('/some-url')
.then((response) => {
return response.json();
})
.then((jsonData) => {
data = jsonData;
})
.finally(() => {
console.log('Cleanup operations');
return data; // This return is problematic. It does not work as you might think.
});
}
-
This rule ensures that the fulfillment or rejection of Promises is consistent by preventing a
return
statement in a.finally()
block, which does not affect the promise’s outcome. This ensures developers do not mistakenly attempt to alter the promise result in afinally
block, which is not possible, leading to clearer and more predictable code behavior. -
By disallowing return statements in
finally
, it avoids confusion regarding the flow of promise resolution and rejection. Sincefinally
is used for cleanup after operations have completed, regardless of the outcome, injecting a return statement might misleadingly imply control over the promise’s resolution value or state which is not the case. -
This rule emphasizes the separation of concerns by ensuring
finally
blocks are used solely for cleanup purposes and not for returning values or affecting the promise chain. It promotes writing cleaner and more maintainable asynchronous JavaScript code by keeping the code responsible for resolving or rejecting promises separate from the code intended for cleanup activities. -
Enforcing no return in
finally
helps in debugging and code readability. When promises do not behave as expected, allowing return statements infinally
could lead to misleading interpretations of the code’s intention. By enforcing this rule, it becomes easier to track down where values are being resolved or rejected in the promise chain, reducing potential debugging time and improving maintainability.
const doSomething = () => {
return new Promise((resolve, reject) => {
doAnotherThing()
.then(result => {
return Promise.resolve(result); // Poor practice
})
.catch(error => {
return Promise.reject(error); // Poor practice
});
});
};
- Ensures that promises are used efficiently by discouraging unnecessary wrapping of return values in
Promise.resolve
orPromise.reject
within aPromise
chain, which can lead to code that is more complex and harder to read than necessary. - Encourages more idiomatic JavaScript coding practices by promoting the return of the value directly or throwing an error, rather than wrapping them, aligning with how Promises are designed to work.
- Helps in minimizing runtime overhead introduced by redundant promise wrapping. Each unnecessary call to
Promise.resolve
orPromise.reject
creates an additional unnecessary promise that must be resolved or rejected, potentially impacting performance. - Improves error stack clarity by reducing the layers of promises, making debugging easier. Unnecessary promise wrapping can obscure the origin of an error, whereas directly throwing an error can make it easier to trace back to its source.
// Example where parameters don't match the expected pattern '^_?resolve$' for the resolve parameter
// and '^_?reject$' for the reject parameter.
new Promise(function(a, b) {
if (Math.random() > 0.5) {
a('Success!');
} else {
b('Error!');
}
});
- Ensures consistency in naming the resolve and reject parameters in Promise constructors, which improves readability and maintainability of promise-related code by adhering to a recognizable pattern.
- Helps prevent bugs by making it clearer which parameter is meant for resolving the promise and which for rejecting, reducing the chance of mistakenly swapping these functionalities due to ambiguous naming.
- Facilitates code reviews and onboarding of new developers by establishing a clear and project-wide naming convention for Promise parameters, making the codebase easier to understand.
- Allows customization of the naming patterns through options, making the rule adaptable to different project conventions or naming schemes while still enforcing consistency.
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: 'John Doe' };
callback(null, data);
}, 1000);
}
fetchData((err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
- Encourages asynchronous code consistency by favoring
await/async
over traditional callback patterns, which can lead to clearer and more maintainable code structures. - Aims to reduce the complexity and nesting often associated with callback functions, potentially decreasing the risk of callback hell and improving code readability.
- By promoting the use of
await/async
, it implicitly supports the handling of promises, which aligns with modern JavaScript practices and facilitates error handling usingtry/catch
blocks. - Helps in identifying and reporting the usage of callbacks in scenarios where promises or async/await constructs could be more appropriate, nudging developers towards adopting a more modern asynchronous programming paradigm.
fetchData()
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
-
This ESLint rule encourages the use of
async/await
overthen()/catch()/finally()
with promises, which can make code easier to read and maintain by reducing the complexity associated with promise chaining. The use ofasync/await
often leads to cleaner and more straightforward code that resembles synchronous code structures, improving readability. -
The rule includes a check to ensure that it only reports
.then()
,.catch()
, or.finally()
usage when not within anasync
function or a generator function (yield
). This specificity helps prevent false positives in contexts whereawait
cannot be used directly, such as at the top-level scope in environments where top-levelawait
is not allowed. -
By including a condition to ignore promises within
yield
orawait
expressions, the rule smartly targets only those promise chains that can and should be converted toasync/await
. This focuses the developer’s attention on refactoring promise usages that will most benefit fromasync/await
syntax, without distracting with irrelevant recommendations. -
The rule provides a clear message, “Prefer await to then()/catch()/finally().”, when a violation is detected. This direct feedback educates developers on modern JavaScript best practices, encouraging them to adopt
async/await
in their code. Such specific guidance helps in codebase modernization efforts and aids in maintaining a uniform coding style across a project.
Ensures the proper number of arguments are passed to Promise functions
// Incorrectly passing two arguments to Promise.resolve
Promise.resolve('Success', 'Extra');
// Incorrectly passing two arguments to Promise.reject
Promise.reject(new Error('Failure'), 'Extra');
- Ensures adherence to the ECMAScript specification for Promises by validating the expected number of parameters for methods like
resolve
,reject
,then
, etc., thereby promoting standard-compliant code which leads to more predictable behavior across different JavaScript environments. - Helps in identifying and reporting cases where Promise methods are invoked with an incorrect number of arguments, making it easier for developers to debug and fix potential issues that could lead to Promise misbehavior or runtime errors.
- Prevents common mistakes related to Promise handling, such as passing unnecessary arguments to the
resolve
orreject
functions, which might lead to confusion regarding the outcome of the Promise or the parameters passed to the subsequent.then()
or.catch()
handlers. - Enhances code readability and maintainability by ensuring that Promise-related code adheres to a standardized format, making it easier for new team members to understand the flow of asynchronous operations and reducing the likelihood of bugs related to mismanagement of Promise resolution parameters.