Simplify Error Handling with "Try": A JavaScript Library Inspired by Rust and Golang
Tags: rust, errors, promises, go
Published: 2024-01-16 18:33:00

---
title: Simplify Error Handling with "Try": A JavaScript Library Inspired by Rust and Golang
published: true
description: Try is a Rust-inspired error-handling library, bringing clarity and simplicity to your development workflow.
tags: ["rust", "errors", "promises", "golang"]
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
published_at: 2024-01-16 18:33 +0000
---

Error handling in JavaScript has often been a source of frustration for developers. Unlike languages like Rust and Golang, where errors are explicit and treated as values, JavaScript tends to rely on implicit error handling. This can lead to difficulties in tracking down errors, especially when working with external libraries or integrating your own code.

## The Problem with Implicit Errors in JavaScript

Consider the following JavaScript code:

```js
import { fetchUser } from "./user";
import { leftpad } from "leftpad";

await fetchUser(); // Should we handle the errors?
leftpad(); // Should we handle the errors?
```

In this snippet, errors are not explicit. There's no clear indication of how to handle errors, and your code essentially relies on trust alone. This lack of clarity can lead to unforeseen issues, making it challenging to build robust and reliable applications.

## Introducing "Try": A Solution Inspired by Rust and Golang

The "Try" library aims to address the challenges of error handling in JavaScript by borrowing concepts from Rust and Golang. It introduces a structured approach where errors are treated as values, making them explicit and easier to manage.

### Error Handling in Rust:

```rust
fn main() {
    let result = File::open("hello.txt");

    let greeting_file = match result {
        Ok(file) => file,
        Err(error) => // handle errors,
    };
}
```

### Error Handling in Golang:

```go
f, err := os.Open("filename.ext")
if err != nil {
  // handle errors
}
```

## Why Use "Try" in JavaScript?

Errors in JavaScript can be tricky to handle due to their implicit nature. The "Try" library serves as a wrapper for your functions, enforcing the need to handle errors. Instead of errors being thrown, they are returned as values. This approach not only makes error handling more explicit but also aims to eliminate callback hell (nested `then`) and the tower of doom (try-catch block).

By treating errors as values, "Try" simplifies error handling, reducing the chances of overlooking errors and making your code more robust and maintainable.

## Getting Started with "Try"

To get started with the "Try" library, follow these simple steps:

Available on [Github](https://github.com/eznix86/try)

```bash
bun add @eznix/try
yarn install @eznix/try
pnpm add @eznix/try
npm install @eznix/try
```

## Wrapping Synchronous/Asynchronous Operations

The "Try" library provides functions for both asynchronous and synchronous operations. Here's how you can use it:

```js
import { trySync, tryAsync } from "@eznix/try";

// `fetchUser` is an async function
const tryFetchUserAsync = await tryAsync(fetchUser);
// `fetchUser` is a sync function
const tryFetchUserSync = trySync(fetchUser);
```

## Handling Results with "Try"

The "Try" library offers various methods to handle results effectively:

### Access Successful Value

```js
const user = await tryFetchUserAsync.getOrElse({"id": 1, "name": "jimmy"});
```

### Inspect Error

```js
const error = await tryFetchUserAsync.error();
```

### Recover from Failure

```js
const recoveredTry = await tryFetchUserAsync.recover((error) => defaultUser);
```

### Unwrap Result (Carefully)

```js
const result = await tryFetchUserAsync.result();
console.log(result.isOk()); // true
console.log(result.unwrap());
```

## Examples of Using "TryAsync"

### Basic Example:

```js
// Wrapping a potentially failing asynchronous operation
const fetchUser = async (id: number): Promise<User> => {
    const response = await fetch(`/api/users/${id}`);
    const user = await response.json();
    return user;
};

const tryFetchUser = await tryAsync(fetchUser(123));

// Handling the result:
const user = await tryFetchUser.getOrElse({"id": 1, "name": "Jimmy"});
console.log("User: ", user.name);
// User: Jimmy
```

## Usage

### Plain JavaScript

```js
// Chaining tryAsync to avoid callback nesting

const getUser = async (id) => {
  // API call to fetch user 
};

const getFriends = async (user) => {
  // API call to get user's friends
};

const renderProfile = async (user, friends) => {
  // Render profile page
};

// Without tryAsync
getUser(1)
  .then(user => {
    return getFriends(user) 
      .then(friends => {
        renderProfile(user, friends);
      })
  })
  .catch(err => {
    // Handle error
  });

// With tryAsync
const user = await tryAsync(getUser(1))
  .recover(handleGetUserError)
  .getOrElse({id: 1}); 

const friends = await tryAsync(getFriends(user))
  .recover(handleGetFriendsError)
  .getOrElse([]);

renderProfile(user, friends);
```

### React Example:

```jsx
import React, { useState, useEffect } from 'react';
import { tryAsync } from '@eznix/try';

function MyComponent() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    async function fetchUser() {
      const user = await tryAsync(fetch('/api/user'))
        .recover(handleFetchError)
        .getOrElse(null);
      
      setUser(user);
    }

    fetchUser();
  }, []);

  if (!user) {
    return <p>Loading...</p>;
  }

  return <Profile user={user} />;
}

function handleFetchError(err) {
  console.error(err);
  return null;
}
```
## Conclusion

"Try" brings the clarity of explicit error handling to JavaScript, inspired by the patterns seen in Rust and Golang. By incorporating it into your projects, you enhance code reliability and make error management more straightforward.

## Contributing

The project is open for usage. Feel free to contribute and enhance it.

## Quick tip

You can wrap any function with `trySync` or `tryAsync`. Making your code safer. In case you are not sure if you should handle an error or now.

You can also simplify with:

```js
import { trySync as t, tryAsync as at} from "@eznix/try";
const {errors, data} = at(fetchUser).toPromise()

// but trySync and tryAsync is self explicit. Unfortunately
// we cannot use `try` because it is a reserved word in JS :(

```
## Try it today or check it out on github.

Available on [Github](https://github.com/eznix86/try)

```bash
bun add @eznix/try
yarn install @eznix/try
pnpm add @eznix/try
npm install @eznix/try
```

Happy coding!