The finally block is a part of a try…catch…finally statement, which allows you to handle errors
and perform cleanup operations regardless of whether an exception is thrown or not. The finally block is executed regardless of whether
an exception occurs or not, and it is placed after the try and catch blocks.
Having a jump statement, such as return, break, continue, or throw, inside a
finally block can lead to unexpected and undesirable behavior, making your code difficult to understand and maintain. While it’s
not inherently forbidden to use jump statements in finally blocks, it is generally discouraged for the following reasons:
finally block is to ensure cleanup operations and code that must run regardless of the outcome, such as
releasing resources or closing connections. If you use a return statement inside the finally block, it will override any previous
return statements in the try or catch blocks. This can lead to unexpected values being returned from a
function.
break, continue, or even another throw inside the finally block can alter the
normal control flow of the program. This can make it difficult to reason about the behavior of the code and may introduce subtle bugs that are hard to
detect.
return or throw statement inside the finally block causes a new exception or alters the return value, it
can hide or suppress the original exception or return value from the try or catch blocks. This can make it challenging to
identify the actual cause of an error.
finally blocks can be hard to read and understand, especially for other developers who might not be
familiar with the unusual control flow. Such code can lead to maintenance issues and make it harder to debug and maintain the application in the long
run.
This rule reports on all usages of jump statements from a finally block. Even if it’s guaranteed that no unhandled exception can
happen in try or catch blocks, it’s not recommended to use any jump statements inside the finally block
to have the logic there limited to the "cleanup".
async function foo() {
let result, connection;
try {
connection = await connect();
result = connection.send(1);
} catch(err) {
console.error(err.message);
} finally {
if (connection) {
connection.close();
}
return result; // Noncompliant: Jump statement 'return' in the 'finally' block
}
}
While there might be rare cases where using jump statements in a finally block is necessary, it’s generally recommended to avoid
it whenever possible. Instead, use the finally block only for cleanup operations and critical tasks that should always be executed,
regardless of exceptions or return values.
async function foo() {
let result, connection;
try {
connection = await connect();
result = connection.send(1);
} catch(err) {
console.error(err.message);
} finally {
if (connection) {
connection.close();
}
}
return result;
}
No issue will be raised when a void return statement is used as a guard clause to conditionally skip cleanup operations in a
finally block. The condition may be a simple boolean flag, a property access expression (including optional chaining), or a negated
boolean expression.
async function fetchData() {
let cancelled = false;
try {
const response = await fetch('/api/data');
return response.json();
} finally {
if (cancelled) {
return; // Compliant by exception: simple flag guard clause
}
console.log('Cleanup operations');
}
}
async function closePopover() {
try {
await field.form.validate();
} finally {
const errors = field.form.queryFeedbacks({ type: 'error' });
if (errors?.length) {
return; // Compliant by exception: property access guard clause
}
setVisible(false);
}
}
async function fetchResource() {
let isActive = true;
try {
const data = await fetch('/api/data');
processData(data);
} finally {
if (!isActive) {
return; // Compliant by exception: negated boolean guard clause
}
setLoading(false);
}
}
finally block
try...catch
return
throw
break
continue