Why Lodash isFunction Fails on Async Generators
Lodash’s _.isFunction method historically misidentifies
async generator functions (async function*) as
non-functions across modern JavaScript runtimes and specific transpiled
setups. This behavior primarily occurs in environments natively
supporting ECMAScript 2018 (ES9)—such as Node.js 10+ and modern
evergreen browsers—running unpatched versions of Lodash (prior to
v4.17.11), as well as in hybrid environments where Babel and polyfill
libraries alter object tags.
The Root Cause in Lodash's Type Detection
Lodash implements _.isFunction by inspecting the
internal [[Class]] tag of an object using an internal
baseGetTag utility, which wraps
Object.prototype.toString.call(value).
In versions prior to 4.17.11, Lodash matched the tag against an explicit whitelist:
[object Function][object GeneratorFunction][object AsyncFunction][object Proxy]
Because the ECMAScript 2018 specification introduced async generators
after Lodash defined this whitelist,
[object AsyncGeneratorFunction] was absent from the check.
As a result, any object returning that specific tag evaluated to
false.
Environments That Trigger the Misidentification
1. Modern Native JavaScript Engines (ES2018+)
Any runtime with native support for
AsyncGeneratorFunction causes unpatched Lodash versions to
misidentify async generators. Because the engine natively labels these
functions with [object AsyncGeneratorFunction], Lodash
rejects them:
- V8-based environments: Node.js 10.0.0 and higher, Google Chrome 63+, and modern Electron releases.
- SpiderMonkey: Mozilla Firefox 55+.
- JavaScriptCore: Apple Safari 11.1+ and iOS Safari.
In these environments, executing
typeof (async function* () {}) returns
"function", but passing that same declaration to
_.isFunction returns false.
2. Transpiled and Polyfilled Build Environments
Projects using Babel, TypeScript, or Webpack in combination with
runtime polyfills like core-js or
regenerator-runtime encounter this issue in two distinct
ways:
- Full ES2018 Symbol Emulation: When
core-jspolyfillsSymbol.toStringTag, it explicitly assigns the[object AsyncGeneratorFunction]tag to async generator constructors to mirror native engine behavior. When passed into older Lodash bundles, this triggers the missing whitelist check regardless of the underlying browser version. - Non-Function Wrapper Runtimes: Certain
configurations of
regenerator-runtimetranspile async generators into state-machine helper objects rather than native function objects. If the transpiled output exposes an iterator object interface instead of a callable function reference,_.isFunctionwill evaluate tofalsedue to the underlyingtypeofcheck failing.
3. Cross-Realm and Sandboxed Contexts
In environments involving multiple execution realms—such as Node.js
vm modules, Web Workers, or browser
<iframe> elements—functions do not share the standard
library prototypes of the host realm. Lodash relies strictly on
baseGetTag rather than instanceof Function
specifically to handle cross-realm objects. However, when an async
generator originates from another realm in an ES2018+ host, the
cross-realm tag check still yields
[object AsyncGeneratorFunction], triggering the bug.
Resolution
The issue was addressed in Lodash version 4.17.11, which added
[object AsyncGeneratorFunction] to the internal whitelist.
Projects unable to upgrade Lodash can accurately detect async generators
across all environments by using the native JavaScript check:
typeof fn === 'function'