Skip to content

noAsyncPromiseFinally (JavaScript)

Language JavaScript (and super languages)
biome.json
{
"linter": {
"rules": {
"nursery": {
"noAsyncPromiseFinally": "error"
}
}
}
}

Disallow async functions as .finally() callbacks.

The function passed to a promise’s .finally() method runs after the promise settles, whether it succeeded or failed. When this function is async, it returns a promise of its own. If that promise is rejected, the promise returned by .finally() is rejected with that new error, and the original value or error is lost.

This rule reports async functions written directly inside .finally(), and names that refer to an async function declared in the same file, either with a function declaration or a const variable. Imported functions and functions accessed through an object are not checked.

The following code logs cleanup instead of original:

try {
await Promise.reject(new Error("original")).finally(async () => {
throw new Error("cleanup");
});
} catch (error) {
console.log(error.message); // "cleanup"
}
code-block.js:2:57 lint/nursery/noAsyncPromiseFinally ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This async function is passed to .finally().

1 │ try {
> 2 │ await Promise.reject(new Error("original")).finally(async () => {
│ ^^^^^^^^^^^^^
> 3 │ throw new Error("cleanup");
> 4 │ });
│ ^
5 │ } catch (error) {
6 │ console.log(error.message); // "cleanup"

ℹ If this function fails, the promise returned by .finally() is rejected with its error, and the original result or error of the promise is lost.

ℹ Use a function that isn't async. If the cleanup has to be asynchronous, handle its errors inside the function, for example with .catch().

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

promise.finally(async () => {
await cleanup();
});
code-block.js:1:17 lint/nursery/noAsyncPromiseFinally ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This async function is passed to .finally().

> 1 │ promise.finally(async () => {
│ ^^^^^^^^^^^^^
> 2 │ await cleanup();
> 3 │ });
│ ^
4 │

ℹ If this function fails, the promise returned by .finally() is rejected with its error, and the original result or error of the promise is lost.

ℹ Use a function that isn't async. If the cleanup has to be asynchronous, handle its errors inside the function, for example with .catch().

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

async function cleanup() {}
promise.finally(cleanup);
code-block.js:3:17 lint/nursery/noAsyncPromiseFinally ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This async function is passed to .finally().

1 │ async function cleanup() {}
2 │
> 3 │ promise.finally(cleanup);
│ ^^^^^^^
4 │

ℹ The function is declared async here.

> 1 │ async function cleanup() {}
│ ^^^^^
2 │
3 │ promise.finally(cleanup);

ℹ If this function fails, the promise returned by .finally() is rejected with its error, and the original result or error of the promise is lost.

ℹ Use a function that isn't async. If the cleanup has to be asynchronous, handle its errors inside the function, for example with .catch().

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

const cleanup = async () => {};
promise.finally(cleanup);
code-block.js:3:17 lint/nursery/noAsyncPromiseFinally ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This async function is passed to .finally().

1 │ const cleanup = async () => {};
2 │
> 3 │ promise.finally(cleanup);
│ ^^^^^^^
4 │

ℹ The function is declared async here.

> 1 │ const cleanup = async () => {};
│ ^^^^^
2 │
3 │ promise.finally(cleanup);

ℹ If this function fails, the promise returned by .finally() is rejected with its error, and the original result or error of the promise is lost.

ℹ Use a function that isn't async. If the cleanup has to be asynchronous, handle its errors inside the function, for example with .catch().

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

promise.finally(() => {
cleanupSynchronously();
});
function cleanup() {}
promise.finally(cleanup);
// If the cleanup has to be asynchronous, handle its errors so they can't replace the original result.
promise.finally(() => {
return asyncCleanup().catch((error) => {
logCleanupError(error);
});
});