Why is this an issue?

Promises need to be resolved or awaited to return the expected value, otherwise, they return the promise object.

Unresolved promises:

Forgetting to await a promise is a frequent mistake. There are places where the use of a promise object is confusing or unclear because the developer forgot to resolve it.

This rule forbids returning promises where another type is expected such as in:

The rule also reports asynchronous predicates passed to synchronous collection methods. This includes the native Array.prototype.every(), filter(), find(), findIndex(), findLast(), findLastIndex(), and some() methods, as well as the Lodash, lodash-es, and Underscore.js every, filter, find, findIndex, some, and reject functions. These methods evaluate predicate results synchronously and do not await returned promises.

Currently, the library-specific detection applies only to direct calls to these methods. It does not report asynchronous callbacks passed to map() or forEach(), where returning a promise can be intentional, or to dynamically computed methods, fluent chains, and lodash/fp pipeline calls.

What is the potential impact?

Using a promise instead of its resolved value can have unexpected results leading to bugs.

The executor function of a promise can also be an async function. However, this usually denotes a mistake:

Exceptions

This rule can be ignored for promises that you know will always resolve like timers.

await new Promise(resolve => time.setTimeout(1000));

This rule does not flag conditionals that check whether a Promise-typed variable has been initialized when the same variable is assigned within the conditional body. This is a common lazy initialization pattern.

let cachedData;
function getData() {
  if (!cachedData) {
    cachedData = fetchData();
  }
  return cachedData;
}

How to fix it

If you mistakenly treated a promise as its resolved value, you can ensure it is properly resolved by using await or resolve on the promise. In some cases, you may need to use an "immediately invoked function expression" (IIFE):

(async function foo() {
  const result = await bar();
  // work with result
})();

When a predicate performs asynchronous work, await its result before applying the collection operation. A for…​of loop makes the ordering explicit. If concurrent evaluation is safe, resolve all predicate results with Promise.all() before selecting the matching elements. For example:

const results = await Promise.all(
  users.map(async user => ({ user, active: await isActive(user) }))
);
const activeUsers = results
  .filter(({ active }) => active)
  .map(({ user }) => user);

Code examples

Noncompliant code example

const promise = new Promise((resolve, reject) => {
  // ...
  resolve(false)
});
if (promise) {
  // ...
}

Compliant solution

const promise = new Promise((resolve, reject) => {
  // ...
  resolve(false)
});
if (await promise) {
  // ...
}

Noncompliant code example

const p = new Promise(async (resolve, reject) => {
  doSomething('Hey, there!', function(error, result) {
    if (error) {
      reject(error);
      return;
    }
    await saveResult(result)
    resolve(result);
  });
});

await p;

Compliant solution

const p = new Promise((resolve, reject) => {
  doSomething('Hey, there!', function(error, result) {
    if (error) {
      reject(error);
      return;
    }
    resolve(result);
  });
});

const result = await p;
await saveResult(result);

Noncompliant code example

apiCalls.forEach(async (apiCall) => {
  await apiCall.send();
});

Compliant solution

for (const apiCall of apiCalls) {
  await apiCall.send();
}

Noncompliant code example

const activeUsers = users.filter(async user => { // Noncompliant: the synchronous predicate treats the returned promise as truthy
  return isActive(user);
});

Compliant solution

const activeUsers = [];
for (const user of users) {
  if (await isActive(user)) {
    activeUsers.push(user);
  }
}

Noncompliant code example

import _ from "lodash";

const activeUsers = _.filter(users, async user => { // Noncompliant: the synchronous predicate treats the returned promise as truthy
  return isActive(user);
});

Compliant solution

import _ from "lodash";

const activeUsers = [];
for (const user of users) {
  if (await isActive(user)) {
    activeUsers.push(user);
  }
}

How does this work?

In JavaScript, a promise is a mechanism to perform tasks asynchronously. To this end, the language provides the Promise object which represents the eventual completion or failure of an asynchronous operation and its resulting value. A promise can be created with the Promise constructor accepting an executor function as an argument, which has resolve and reject parameters that are invoked when the promise completes or fails.

The logic of the promise is executed when it is called, however, its result is obtained only when the promise is resolved or awaited.

Resources

Documentation