How Lodash _.some Handles an Array of Promises

Lodash’s _.some method processes arrays synchronously, which fundamentally alters how it interacts with asynchronous structures like Promises. When provided an array populated entirely by dynamically resolved Promises, _.some does not await their resolution; instead, it evaluates the Promise instances immediately as standard JavaScript objects. This article explains how Lodash evaluates these collections, why unexpected true results occur, and how to properly handle asynchronous evaluation.

The Synchronous Evaluation of Promises

Lodash collection methods, including _.some, are strictly synchronous. When _.some iterates over an array containing Promises, it passes each raw Promise object directly to the predicate function without pausing execution or waiting for the Promise to settle.

Because every Promise in JavaScript is an object, it evaluates to a truthy value in a boolean context, regardless of whether the internal state of that Promise is pending, fulfilled, or rejected.

Default Predicate Behavior

If no custom predicate is passed to _.some, Lodash defaults to _.identity, which returns the value of the current element:

const p1 = Promise.resolve(false);
const p2 = Promise.resolve(false);

const result = _.some([p1, p2]); 
// result is true

In this scenario:

  1. _.some inspects the first element, p1.
  2. p1 is an instance of Promise, which is an object and therefore truthy.
  3. Because the truth test passes immediately, _.some short-circuits and returns true.
  4. The remaining elements are not evaluated, and the underlying resolved values (even if false) are ignored.

Custom and Asynchronous Predicates

Passing an async predicate function does not fix this behavior. An async function always returns a Promise, which is also truthy.

const values = [Promise.resolve(false), Promise.resolve(false)];

const result = _.some(values, async (p) => {
  const resolved = await p;
  return resolved === true;
});
// result is true

Even though the predicate checks if the resolved value is true, the return value of the async callback itself is a pending Promise. Since that returned Promise is truthy, _.some interprets it as a passing condition on the very first iteration and returns true.

The Proper Way to Process Promises with some

To evaluate an array of dynamically resolved Promises against a condition, the Promises must be awaited before applying the logical test, or handled using asynchronous primitives.

Approach 1: Resolve All Promises First

If all Promises must resolve before determining if at least one satisfies the condition:

const promises = [Promise.resolve(false), Promise.resolve(true)];

const resolvedValues = await Promise.all(promises);
const result = resolvedValues.some((val) => val === true);
// result is true

Approach 2: Short-Circuiting Asynchronously

To mimic the short-circuiting behavior of _.some asynchronously—returning as soon as the first truthy condition is met—wrap the condition in individual Promises and use native Promise.any:

const promises = [Promise.resolve(false), Promise.resolve(true)];

const hasMatch = await Promise.any(
  promises.map(async (p) => {
    const val = await p;
    if (val === true) return true;
    throw new Error("Condition not met");
  })
).then(() => true).catch(() => false);

Using _.some directly on an array of Promises will virtually always evaluate to true due to JavaScript's truthy object semantics. Asynchronous collections must always be awaited prior to passing their values to Lodash iteration methods.