Or Assayag
Or Assayag

Reputation: 6336

Detect missing await in JavaScript methods in VSCode

I'm searching for some eslint option, or some other way to detect missing the 'await' keyword before calling async methods inside a class. Consider the following code:

const externalService = require('./external.service');

class TestClass {

constructor() { }

async method1() {
    if (!await externalService.someMethod()) {
        await this.method2();
    }
}

async method2() {
    await externalService.someOtherMethod();
}

module.exports = TestClass;

There will be no warning if I will convert method1 to:

async method1() {
    if (!await externalService.someMethod()) {
        this.method2();
    }
}

I tried to do on the '.eslintrc' file:

"require-await": 1,
"no-return-await": 1,

But with no luck. Anyone have an idea if it is even possible? Thanks a lot!

Upvotes: 12

Views: 7767

Answers (4)

bmaupin
bmaupin

Reputation: 15995

typescript-eslint has a rule for this: no-floating-promises

This rule forbids usage of Promise-like values in statements without handling their errors appropriately ... Valid ways of handling a Promise-valued statement include awaiting, returning, and either calling .then() with two arguments or .catch() with one argument.

As you probably figured out from the name, typescript-eslint is designed to add TypeScript support to eslint, but you can use it with JavaScript as well. I guess it's your call to decide whether it's overkill for this one rule, but here are the steps:

  1. Generate a tsconfig.json file

    npx tsc --init
    
  2. Install dependencies

    npm install --save-dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser
    
  3. Modify your .eslintrc file

    Based on my testing it looks like you'll need these entries at a minimum:

    {
      "parser": "@typescript-eslint/parser",
      "parserOptions": { "project": "./tsconfig.json" },
      "plugins": ["@typescript-eslint"],
      "rules": {
        "@typescript-eslint/no-floating-promises": ["error"]
      }
    }
    

If there are places where you want to call an async function without using await, you can either:

  • Use the void operator as mentioned in the documentation for the rule, e.g.

    void someAsyncFunction();
    
  • Or just change error to warn in the .eslintrc configuration above

The documentation for setting up typescript-eslint is here for more info: https://typescript-eslint.io/docs/linting/linting

Next time you run eslint you should see the rule applied:

$ npm run lint
...
./services/jobService.js
  11:5  warning  Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator  @typescript-eslint/no-floating-promises

Since you mentioned VS Code specifically, this also integrates great with the ESLint plugin:

vscode showing no-floating-promises rule

Upvotes: 15

nmDat
nmDat

Reputation: 769

Consider using the combination of eslint rules for a better version of checking await:

No-floating-promises

return-await with try-catch option to able catch the Promise rejection (reference)

Upvotes: 1

Magnus
Magnus

Reputation: 788

There is an ESLint plugin for this that you can use as an alternative to the typescript-eslint route:

https://github.com/SebastienGllmt/eslint-plugin-no-floating-promise

Upvotes: 1

Quentin
Quentin

Reputation: 943591

require-await says "Don't make a function async unless you use await inside it".

This is because async has two effects:

  • It forces the function to return a promise
  • It lets you use await inside it

The former is rarely useful which mean if you aren't using await inside the function you need to question why you marked it as async.


no-return-await stops you from doing:

return await something

Because await unwraps a value from a promise, but returning a value from an async function wraps it in a promise.

Since just returning a promise causes that promise to be adopted, combining return with await is just bloat.


So neither of those do what you want.

Which brings us to your actual desire.

Such a feature does not (as far as I know) exist in ESLint, and I don't think it would be useful to have one.

There are lots of use cases where you don't want to await something returned by an async function.

e.g.

const array_of_promises = array_of_values.map( value => do_something_async(value) );
const array_of_resolved_values = await Promise.all(array_of_promises);

The above is a common use-case where you want to run a bunch of async functions in parallel and then wait for them all to resolve.

Another example is the case that no-return-await is designed to detect!

Cases like these are common enough that most people wouldn't want their toolchain calling them out for doing it.

Upvotes: 3

Related Questions