Lodash _.attempt: Handle Errors Without Try-Catch

The Lodash _.attempt method provides a clean, functional approach to executing code that might throw errors without requiring explicit try...catch blocks in your application logic. By abstracting exception handling into a single function call, it captures any thrown exceptions and returns either the function's result or the caught Error object. This guide explains how _.attempt works under the hood, how to validate its output, and how to effectively use it in JavaScript applications.

How _.attempt Works

In standard JavaScript, code prone to runtime exceptions—such as parsing untrusted JSON or accessing deeply dynamic operations—must be wrapped in a try...catch block. This often leads to verbose and deeply nested code.

The _.attempt function solves this by encapsulating the try...catch logic internally. When you pass a function to _.attempt, it performs the following sequence:

  1. Wraps the invocation of the provided function within its own internal try block.
  2. Applies any supplied arguments to the target function.
  3. Returns the resulting value if execution succeeds.
  4. Catches any exception thrown during execution and returns that Error object directly instead of letting it bubble up and crash the runtime.

Syntax and Usage

The syntax for _.attempt accepts a function followed by any arguments to be passed to that function:

_.attempt(func, [args])

A common use case is parsing JSON data, which natively throws a SyntaxError when given malformed input:

const _ = require('lodash');

// Valid JSON
const validData = _.attempt(JSON.parse, '{"name": "Alice"}');
console.log(validData); // Output: { name: 'Alice' }

// Invalid JSON
const invalidData = _.attempt(JSON.parse, '{invalid_json}');
console.log(invalidData); // Output: [SyntaxError: Unexpected token i in JSON at position 1]

Inspecting the Return Value with _.isError

Because _.attempt returns either the successful output or an instance of an error, you must inspect the returned value to determine if the operation succeeded. Lodash provides the _.isError utility specifically for this check:

const result = _.attempt(JSON.parse, rawInput);

if (_.isError(result)) {
  console.error('Failed to parse input:', result.message);
} else {
  console.log('Successfully parsed data:', result);
}

This pattern mirrors error-handling conventions found in languages like Go, where errors are treated as normal values rather than disrupted control flows.

Synchronous Execution Limitation

It is important to note that _.attempt only intercepts synchronous errors. It cannot catch unhandled promise rejections or errors occurring inside asynchronous callbacks:

// This will NOT catch errors inside the Promise
const asyncResult = _.attempt(async () => {
  throw new Error('Async failure');
});

// asyncResult will be a rejected Promise, not an Error object

For asynchronous operations, native Promise.prototype.catch() or async/await with traditional handling must be used instead.