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 trueIn this scenario:
_.someinspects the first element,p1.p1is an instance ofPromise, which is an object and therefore truthy.- Because the truth test passes immediately,
_.someshort-circuits and returnstrue. - 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 trueEven 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 trueApproach 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.