eslint-plugin-no-throw-await

1.0.0 • Public • Published

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 🔧 💡

Package Sidebar

Install

npm i eslint-plugin-no-throw-await

Weekly Downloads

3

Version

1.0.0

License

ISC

Unpacked Size

11.3 kB

Total Files

7

Last publish

Collaborators

  • wes4m