By the end of this tutorial, you’ll invoke risky functions safely with Lodash’s _.attempt() and handle failures using _.isError()—without scattering try/catch blocks everywhere.
01
Core Syntax
Call _.attempt(func, ...args) and inspect the return value.
02
Value or Error
Success returns the function result; failure returns the caught Error.
03
_.isError()
Distinguish errors from normal return values in one check.
04
Pass Arguments
Forward parameters after the function reference.
05
Functional Style
Use inside _.map() for batch safe transforms.
06
When Not to Use
Know when native try/catch or async patterns fit better.
Fundamentals
What Is _.attempt()?
_.attempt() is a Lodash util helper that calls a function inside a try/catch and returns either the function’s return value or the caught error object. Instead of wrapping every risky call in a statement block, you get a single expression you can assign, pass to another function, or map over an array.
💡
Beginner tip
Think of _.attempt(JSON.parse, text) as “try to parse this string; if it fails, give me the Error object instead of crashing.” Then use _.isError(result) to branch.
This pattern shines when a failure is expected and recoverable—invalid user input, optional plugin callbacks, or per-row data transforms where one bad row should not stop the batch.
Foundation
📝 Syntax
The signature accepts a function plus optional arguments forwarded to that function:
javascript
_.attempt(func, [args])
Syntax Rules
func — the function to invoke (not the result of calling it).
args — optional values passed to func after the first argument.
Return value — the function’s return value on success, or the caught error on failure.
Error check — use _.isError(value) to detect failure results.
Synchronous only — _.attempt() does not await Promises; use try/catch with async/await for async work.
javascript
import attempt from "lodash/attempt";
import isError from "lodash/isError";
function riskyOperation() {
throw new Error("Something went wrong");
}
const result = attempt(riskyOperation);
if (isError(result)) {
console.error("An error occurred:", result.message);
} else {
console.log("Operation result:", result);
}
Cheat Sheet
⚡ Quick Reference
Task
Code pattern
Result
Call with no args
_.attempt(fn)
Return value or Error
Call with arguments
_.attempt(parseInt, "42", 10)
Forwards args to fn
Check for failure
_.isError(result)
true when result is an Error
Safe JSON parse
_.attempt(JSON.parse, str)
Object or SyntaxError
Map over items
_.map(items, i => _.attempt(fn, i))
Array of values/errors
Native equivalent
try { fn() } catch (e) { e }
Statement-based
Throws?
No
Returns the Error
Async
No
Sync functions only
Pair with
_.isError
Detect failures
Category
Util
General helpers
Reference
🧰 Parameters
Every argument to _.attempt() and what it controls:
funcRequired
The function to invoke. Pass the function reference itself—do not call it first unless you intend to pass its return value (usually wrong for attempt).
_.attempt(JSON.parse, text)
argsOptional
Zero or more arguments forwarded to func. Lodash applies them with func.apply(undefined, args).
_.attempt(parseInt, "ff", 16)
return valueValue | Error
On success, whatever func returned (including undefined). On failure, the caught error object—typically an Error instance.
const r = _.attempt(fn)
_.isError()Companion
Lodash helper that returns true for Error-like objects. Use it immediately after _.attempt() to branch between success and failure paths.
if (_.isError(r)) { ... }
_.attempt() never re-throws the error—it always returns it. Your calling code decides whether to log, fallback, or propagate manually.
Hands-On
Examples Gallery
Practical _.attempt() patterns with copy-ready code, sample output, and interactive Try It Yourself labs.
📚 Getting Started
Catch thrown errors as return values and handle them with _.isError().
Example 1 — Handle a throwing function
When riskyOperation throws, _.attempt() returns the Error instead of crashing your script.
javascript
function riskyOperation() {
throw new Error("Something went wrong");
}
const result = _.attempt(riskyOperation);
if (_.isError(result)) {
console.error("An error occurred:", result.message);
} else {
console.log("Operation result:", result);
}
// -> "An error occurred: Something went wrong"
Lodash wraps the call in try/catch internally. The thrown Error becomes the return value, so execution continues in the same function scope.
Example 2 — Successful return path
When the function completes normally, you get its return value and _.isError() is false.
javascript
function add(a, b) {
return a + b;
}
const result = _.attempt(add, 10, 32);
if (_.isError(result)) {
console.error("Failed:", result.message);
} else {
console.log("Sum:", result);
}
// -> "Sum: 42"
📤 Console output:
Sum: 42
How It Works
Extra arguments after add are forwarded as add(10, 32). No exception means the numeric result passes through unchanged.
📈 Practical Patterns
Defensive parsing, argument forwarding, and batch transforms in collections.
Example 3 — Forward arguments safely
Pass the target function plus its parameters—useful for built-ins like parseInt that may throw or return unexpected values.
javascript
const good = _.attempt(parseInt, "42", 10);
const bad = _.attempt(parseInt, "hello", 10);
console.log("good:", good); // 42
console.log("bad:", bad); // NaN (parseInt does not throw)
console.log("isError(good):", _.isError(good)); // false
📤 Console output:
good: 42
bad: NaN
isError(good): false
How It Works
_.attempt() only catches thrown exceptions. Functions that return sentinel values like NaN still count as success—you may need extra validation after the call.
Example 4 — Safe JSON.parse on user input
Invalid JSON throws a SyntaxError. Wrap parsing with _.attempt() and fall back to a default object.
JSON.parse throws on malformed input. _.attempt() converts that throw into a return value you can handle with a simple fallback—ideal for localStorage or query-string config strings.
Example 5 — Map over items without stopping the batch
Transform each row safely; collect errors separately so one bad record does not abort the loop.
javascript
function parseRow(row) {
if (!row.payload) {
throw new Error("Missing payload on row " + row.id);
}
return JSON.parse(row.payload);
}
const rows = [
{ id: 1, payload: '{"ok":true}' },
{ id: 2, payload: null },
{ id: 3, payload: '{"count":5}' }
];
const results = _.map(rows, function (row) {
return _.attempt(parseRow, row);
});
const ok = [];
const failed = [];
_.forEach(results, function (result, index) {
if (_.isError(result)) {
failed.push({ id: rows[index].id, error: result.message });
} else {
ok.push({ id: rows[index].id, data: result });
}
});
console.log("ok:", ok);
console.log("failed:", failed);
Use _.attempt() inside higher-order functions where a try/catch block would require extracting a helper. For straightforward async I/O, prefer async/await with try/catch instead.
Compare
📋 _.attempt vs related patterns
Topic
_.attempt
try/catch
Promise.catch
_.over
Style
Expression (returns value)
Statement block
Async chain
Run multiple fns
Sync/async
Synchronous only
Both (with async fn)
Promises only
Synchronous
On throw
Returns Error object
Jumps to catch block
Rejects or catches
Each fn runs independently
Best in
_.map / pipelines
Imperative flow
fetch / async APIs
Parallel getters
Check result
_.isError()
catch (e)
.catch(fn)
Inspect each return
🧠 How _.attempt() Works
1
Receive func + args
Lodash collects the function reference and any trailing arguments.
Input
2
Try invoke
Calls func.apply(undefined, args) inside an internal try block.
Execute
3
Catch or return
On success, returns the function result. On throw, returns the caught value (usually an Error).
Result
=
✅
No re-throw
Your code inspects the return value with _.isError() and decides the next step.
Important
📝 Notes
_.attempt() is for synchronous functions—it does not await Promises returned by async functions.
Always pair with _.isError() when the function might throw—do not assume every failure is an Error instance.
Functions that signal failure via return values (like NaN) still count as success unless they throw.
Pass the function reference, not fn(), unless you deliberately want to attempt whatever fn() returned.
For network or file I/O, use async patterns; wrapping fetch() in _.attempt() only catches synchronous setup errors, not HTTP failures.
Related util helpers: _.over() runs multiple functions; _.cond() picks branches by predicate.
Wrap Up
Conclusion
_.attempt() turns thrown exceptions into return values so you can handle errors as data. Combined with _.isError(), it keeps functional pipelines and collection transforms readable when some inputs are expected to fail.
Reach for native try/catch or async/await when control flow is linear and asynchronous. Use _.attempt() when a single expression fits better— especially inside _.map() over heterogeneous data.
Assume HTTP errors from fetch—failed responses do not throw by default
Replace all try/catch blocks when a statement reads clearer
Ignore non-Error thrown values—validate with _.isError() first
Call riskyFn() instead of passing riskyFn unless intentional
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about _.attempt()
Use these points whenever you need safe synchronous function calls.
5
Core concepts
🛡️01
Safe call
Throws become return values.
Basics
🔍02
isError
Detect failure results.
Pattern
📄03
JSON.parse
Classic use case.
Practical
📊04
Map batch
One bad row OK.
Scale
⚡05
Sync only
Use async/await elsewhere.
Limit
❓ Frequently Asked Questions
_.attempt() calls a function inside an internal try/catch. If the call succeeds, it returns the function's return value. If the function throws, it returns the caught error object instead of propagating the exception.
try/catch uses control flow with a block statement. _.attempt() returns either a value or an Error in one expression, which is convenient inside _.map(), _.flow(), or functional pipelines where a statement block would be awkward.
Use _.isError(result). When true, result is the caught Error and you can read result.message. When false, result is the normal return value from your function.
Yes. Arguments after the function are forwarded: _.attempt(parseInt, '42', 10) calls parseInt('42', 10). This works the same as func.apply(undefined, args).
Use it for small, isolated risky calls—JSON.parse on user input, optional callbacks, or per-item transforms in a collection—where you want a value-or-error result without nested try/catch blocks.
Lodash catches any thrown value. _.isError() returns true only for Error-like objects. If someone throws a string or number, handle that with typeof checks or prefer throwing proper Error instances in your own code.
Did you know?
_.attempt() is the expression-oriented cousin of try/catch. Lodash also ships _.isError() in the same util category—it checks for Error-like objects so you can branch without instanceof Error quirks across iframes or realms.