How Lodash Filter Handles Async Generators

This article examines how the Lodash utility library processes asynchronous generators passed to its _.filter method. Because Lodash is designed exclusively for synchronous data processing, passing an asynchronous generator does not yield the expected filtered stream or promise; instead, Lodash treats the generator object as a plain, empty object, resulting in an empty array.

The Synchronous Architecture of Lodash

Lodash’s _.filter is fundamentally synchronous. It accepts a collection (such as an array or a plain object) and an iteratee function. Internally, Lodash inspects the collection to determine how to traverse it:

Lodash does not support the ECMAScript asynchronous iteration protocol (Symbol.asyncIterator), which asynchronous generators rely on.

Why Async Generators Return an Empty Array

When an async generator function is invoked, it returns an AsyncGenerator object. This object does not contain a length property, nor does it have enumerable own properties. Instead, it exposes methods like next(), return(), and throw() via its prototype chain.

Because the generator object has no length property, _.filter routes it through its internal object-iteration path. When Lodash attempts to iterate over the object’s own enumerable properties, it finds none. Consequently:

  1. The filtering loop terminates immediately without executing any iterations.
  2. The iteratee/predicate function is never called.
  3. The generator is never prompted to yield values via its .next() method.
  4. _.filter immediately and synchronously returns an empty array ([]).

The Problem with Asynchronous Predicates

Even if values could be extracted, _.filter cannot handle asynchronous predicates. If a predicate returns a Promise, Lodash evaluates the returned Promise object directly in a synchronous boolean context. Because all objects (including unresolved Promises) are truthy in JavaScript, any async predicate would cause every evaluated item to pass the filter, rather than waiting for the resolved boolean value.

Proper Alternatives for Filtering Async Generators

To filter items yielded by an asynchronous generator, native JavaScript constructs must be used instead of Lodash:

async function* filterAsyncGenerator(asyncGen, predicate) {
  for await (const item of asyncGen) {
    if (await predicate(item)) {
      yield item;
    }
  }
}

Alternatively, if the data set is finite and memory allows, you can collect the asynchronous generator's items into a standard array first using a for await...of loop, and then pass that populated array to _.filter synchronously.