20

We've deployed bugs in our node app b/c we forgot to prefix async function calls with "await".

Example:

const getUsers = async () => db.query('SELECT * from Users');

const testMissingAwait = async () => {
  const users = getUsers(); // <<< missing await
  console.log(users.length);
};

testMissingAwait();

Is there an easy way to find async function calls missing the await keyword?

Failing that, how much effort would it be to write a Visual Studio Code extension that flags these automatically? (I'm up for tackling if anyone can point me in the right direction).

Kevin B
  • 94,570
  • 16
  • 163
  • 180
1905home
  • 301
  • 2
  • 3
  • does that function even need to be async? does it gain anything at all? (getUsers) – Kevin B Oct 17 '17 at 20:08
  • 2
    Are you using typescript in this project? If so, it should have captured that error at compile since the type `Promise` has no property 'length'. – CRice Oct 17 '17 at 20:12
  • @CRice that works if you do "users.length" because Promise has no property 'length', but if you forget to await an async function that has no return value, then Typescript won't help you with this mechanism. Does anyone know if there's a Typescript setting (or other linter) that can detect async function calls that you're not awaiting and flag them as possible errors (that you could use a directive to explicitly ignore only when you want to kick off async work and not wait for it)? – Patrick Finnigan Oct 27 '17 at 15:07
  • Actually looks like there's a tslint rule to detect "floating" promises called no-floating-promises - https://palantir.github.io/tslint/rules/no-floating-promises/, https://github.com/palantir/tslint/pull/1632 – Patrick Finnigan Oct 27 '17 at 15:21
  • Is there something in ESLint for this, too? `require-await` isn't it. – user1278519 Sep 17 '18 at 05:51
  • @user1278519 did you find a solution for eslint? – Prinzhorn Oct 17 '18 at 12:10

3 Answers3

6

TypeScript compiler doesn't provide a compiler option for that. However, TSLint 4.4 provides an option to detect floating promises. You can read this blog post for more detailed answer: Detect missing await in typescript

Download TSLint:

npm install -g tslint typescript

Configure TSLint:

{
    "extends": "tslint:recommended",
    "rules": {
        "no-floating-promises": true
    }
}

Run TSLint:

tslint --project tsconfig.json

If there are floating promises in your code, you should see the following error:

ERROR: F:/Samples/index.ts[12, 5]: Promises must be handled appropriately

meziantou
  • 20,589
  • 7
  • 64
  • 83
  • 1
    Yes, that works, but VSCode problems tab shows less errors than running tslint manually (for example tslint error when you forgot the await is not present in problems tab) – Eugenio Mar 21 '18 at 18:04
  • 1
    I also had VS Code not showing all errors that ESLint cli could detect. Installing the ESLint VS Code plugin fixed that. – Max Ivanov Oct 15 '20 at 22:33
3

TypeScript already does this

// users is a Promise<T>
const users = getUsers(); // <<< missing await

// users.length is not a property of users... then and catch are
console.log(users.length);

You can find situations where you won't be told about your mistake - where the types are compatible, for example I missed an await here:

function delay(ms: number) {
    return new Promise<number>(function(resolve) {
        setTimeout(() => {
            resolve(5);
        }, ms);
    });
}


async function asyncAwait() {
    let x = await delay(1000);
    console.log(x);

    let y = delay(1000);
    console.log(y);

    return 'Done';
}

asyncAwait().then((result) => console.log(result));

Because console.log doesn't cause any type incompatibility between my numbers and my promises, the compiler can't tell I have made a mistake.

The only solution here would be a type annotation... but if you're gonna forget an await, you're just as likely to forget a type annotation.

let y: number = delay(1000);
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • 2
    This only works if you expect a return value from the Promise-returning function. If you instead call the function without assigning the result to a variable but still need to `await` it to ensure things happen in the right order, you won't get notified about your mistake. – Clonkex Mar 10 '22 at 01:49
3

tslint is deprecated but 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.

To use it, you'll need to set up typescript-eslint:

  1. Install dependencies

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

    These are the minimum required settings; see the documentation for more options (https://typescript-eslint.io/docs/linting/linting):

    {
      "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

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

$ npm run lint
...
./src/database/database.ts
  219:7  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

bmaupin
  • 14,427
  • 5
  • 89
  • 94