eslint-plugin-no-throw-await
v1.0.0
Published
Forces explicit error handling for promises (Similar to golang) through disallowing unpredictable awaits.
Downloads
6
Maintainers
Readme
eslint-plugin-no-throw-await
Forces explicit error handling for promises (Similar to golang) through disallowing unpredictable awaits
Why?
await
can't be trusted. and try-catching everything is 💩. Explicit error handling is the way. It doesn't clutter your code it makes it better. Take a look at the following code:
async function foo() {
await bar()
}
Is bar
safe to await? does it throw an exception maybe? Just in case let's wrap it in a try-catch
async function foo() {
try {
await bar()
} catch (e) {
/* whatever */
}
}
Now assume you don't know what foo
does. Or you don't want to read every async function line by line to check if it may throw an exception before using it. So what do you do? Also wrap foo
in a try-catch just in case
try { await foo() } catch (e) { }
When/how do you propgate an error to the caller? or do you silence everything throuh a try catch? What if you have a series of async functions. But you don't want one throw to stop everything. Do you just wrap every single one in a try-catch. Or worse, use .catch
for a quick nesting hell trip. There are many other examples of how bad this trycatching can get, amongst other issues with throwing in an async func.
The goal of this plugin is to treat every promise as unsafe, which they are, and only allow awaiting a safe promise. A safe promise in this case means one that will not crash the application if left outside of a try-catch (will never throw). To to that, a linter rule will prevent you from awaiting a promise unless it's wrapped by an awaitable
function.
awaitable
Turns any unsafe promise into safe promise. One implementation (golang like error handling):
/**
* Guarantees that a promise throw will be handled and returned gracefully as an error if any
* The returned promise will never throw an exception
* Result and error type can be specified through awaitable<ResultType, ErrorType>
* @param fn Promise
* @returns Promise<[result, error]>
* - `result`: Returned on success, null on throw (Infered type of `fn`)
* - `error`: Null on success, returned on throw (Default to Error)
*/
/* Modified version from karanpratapsingh */
async function awaitable<R, E = Error> (
fn: Promise<R>
): Promise<[R | null, E | null]> {
try {
// eslint-disable-next-line no-throw-await/no-direct-await
const data: R = await fn
return [data, null]
} catch (error: any) {
return [null, error]
}
}
Example
async function foo (): Promise<boolean> {
throw new Error('Some error')
}
async function testing (): Promise<void> {
const [result, err] = await awaitable(foo())
if (err != null) {
console.log(err.message)
return
}
// Do stuff with result
console.log(result)
}
Installation
You'll first need to install ESLint:
npm i eslint --save-dev
Next, install eslint-plugin-no-throw-await
:
npm install eslint-plugin-no-throw-await --save-dev
Usage
Add no-throw-await
to the plugins section of your .eslintrc
configuration file. You can omit the eslint-plugin-
prefix:
{
"plugins": [
"no-throw-await"
]
}
Then configure the rule under the rules section.
{
"rules": {
"no-throw-await/no-direct-await": "error"
}
}
Rules
🔧 Automatically fixable by the --fix
CLI option.
💡 Manually fixable by editor suggestions.
| Name | Description | 🔧 | 💡 | | :----------------------------------------------- | :------------------------------------------------- | :- | :- | | no-direct-await | Enforces using an await handler before every await | 🔧 | 💡 |