# Function.prototype.try for error handling

**URL:** <https://es.discourse.group/t/function-prototype-try-for-error-handling/2016>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [April 16, 2024, 8:19pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016 "2024-04-16T20:19:57Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![helloworld228](https://avatars.discourse-cdn.com/v4/letter/h/e79b87/32.png) [@helloworld228](https://es.discourse.group/u/helloworld228)\
**Post date:** [April 16, 2024, 8:19pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/1 "2024-04-16T20:19:57Z")

</div>

## Rationale

By introducing `Function.prototype.try`, developers can manage errors in a structured and consistent format, similar to the approach seen in Go. This method allows errors to be treated as regular return values, which encourages more robust error handling practices and prevents errors from being overlooked.

## Description

The proposed `Function.prototype.try` method encapsulates function execution within an implicit try-catch block, returning a `Result` object that contains the fields `value`, `error`, and an `isError` boolean flag. For asynchronous operations, the method returns a `Promise` that resolves to a `Result` object, ensuring that error handling remains consistent across both synchronous and asynchronous contexts.

```js
const result = JSON.parse.try("}"); // no destructuring
const { error, value, isError } = JSON.parse.try("}"); // object destructuring
const [err, json, isErr] = await fetch.try("http://localhost/"); // array destructuring
const [err] = await doSomethingAsync(); // array destructuring

```

## Example usage

```js
const [fetchError, response] = await fetch.try('https://api.example.com/data');
if (fetchError) {
    console.log('Failed to fetch data:', fetchError);
    return;
}

const { value: data, isError } = await response.json.try();
if (parseResult.isError) {
    console.log('Failed to parse JSON');
    return;
}

console.log('Received data:', data);

```

## Working (naive) implementation

```js
class Result {
    error = undefined;
    value = undefined;
    isError = false;

    // this will allow to use both object and array destructuring
    [Symbol.iterator] = function* () {
        yield this.error;
        yield this.value;
        yield this.isError;
    }
}

Function.prototype.try = function (thisArg, ...args) {
    const result = new Result();

    try {
        const promiseOrValue = this(...args);

        if (promiseOrValue instanceof Promise) {
            return promiseOrValue
              .then(value => {
                result.value = value;
                return result;
              })
              .catch(error => {
                result.error = error;
                result.isError = true;
                return result;
              });
        }

        result.value = promiseOrValue;
        return result;
    }
    catch (error) {
        result.error = error;
        result.isError = true;
        return result;
    }
};

```

Source: [GitHub - alex-solovev/proposal-function-try](https://github.com/alex-solovev/proposal-function-try)

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/senocular/32/642_2.png) [@senocular](https://es.discourse.group/u/senocular)\
**Post date:** [April 16, 2024, 8:58pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/2 "2024-04-16T20:58:46Z")

</div>

Related discussion:

> [@Try-catch oneliner](https://es.discourse.group/t/try-catch-oneliner/107):
>
> There is a lot libraries in npm doing similar stuff, if you search [try await](https://www.npmjs.com/search?q=try%20await) you will see a lot libraries which do mostly the same: const [error, result] = tryCatch(JSON.parse, 'hello'); What all this people want is to use more clean syntax for error handling, similar to something [Go already has](https://blog.golang.org/error-handling-and-go). So instead of using such a big construction: let error, data; try { data = JSON.parse('hello'); } catch (e) { error = e; } where you need to use let, 2 code blocks and 3 variables. What I wan…

---

<div class="post-metadata">

**Author:** ![helloworld228](https://avatars.discourse-cdn.com/v4/letter/h/e79b87/32.png) [@helloworld228](https://es.discourse.group/u/helloworld228)\
**Post date:** [April 16, 2024, 9:12pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/3 "2024-04-16T21:12:26Z")

</div>

Hey, thanks @senocular I've read through it, seems like most of the focus was directed towards `try` operator. I think that a method on the `Function.prototype` would be easier to justify. On top of that my proposition improves on return value semantics by allowing both object/array destructuring (or none at all).

It is sad to see that the interest to this kind of feature has died out (the latest message from Delegate is 2 years old), hopefully this proposal can change that.

---

<div class="post-metadata">

**Author:** ![lightmare](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/lightmare/32/843_2.png) [@lightmare](https://es.discourse.group/u/lightmare)\
**Post date:** [April 17, 2024, 9:23am UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/4 "2024-04-17T09:23:42Z")

</div>

Nice, this could have a much lower bar for acceptance than `try` expression syntax.

Unfortunatelly, it wouldn't work with methods:

```js
let res = o.m.try(x) // m called with this !== o

```

You'd have to use something like:

```js
let res = o.m.bind(o).try(x)

```

I would also not include the `isError` property. I'm aware that one can `throw` falsy values, including `undefined`, but I believe that practice should be actively discouraged.

---

<div class="post-metadata">

**Author:** ![helloworld228](https://avatars.discourse-cdn.com/v4/letter/h/e79b87/32.png) [@helloworld228](https://es.discourse.group/u/helloworld228)\
**Post date:** [April 17, 2024, 10:06am UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/5 "2024-04-17T10:06:40Z")

</div>

Hey @lightmare thank you, I appreciate your feedback!

I think that binding issue could be easily solved by using `thisArg` argument, similar to what `call`, `bind` and `apply` do (as well as bunch of `Array` methods e.g. `forEach`, `map`, `filter` etc.). You would be able to call it like so:

```js
let res = o.m.try(o, x)

```

The example implementation would change to:

```js
Function.prototype.try = function (thisArg, ...args) { // 1. add thisArg
    const func = this; // this is just for readability
    const result = new Result();

    try {
        const promiseOrValue = func.call(thisArg, ...args); // 2. call function with thisArg

```

As for the `Result.isError` – I'm also not such a big fan of it, however I believe that there should be an escape hatch in case when falsy values were thrown.

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [April 17, 2024, 2:05pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/6 "2024-04-17T14:05:44Z")

</div>

> [@lightmare](#):
>
> Unfortunatelly, it wouldn't work with methods:
> 
> ```javascript
> let res = o.m.try(x) // m called with this !== o
> 
> ```

One alternative to consider is a new syntax, something like

```javascript
let res = o.m@.(x)

```

where the `@.( )` operator (syntax modeled after the `?.( )` conditional invocation operator and the proposed `~.( )` bind operator, borrowing semantic meaning from PHP's `@` error-suppression operator) is a modified function/method call operator that has the semantic meaning of the `Function.prototype.try` method you're describing, only it works equally well for method calls.

It might be that TC39 isn't inclined to use up syntactic real-estate with an operator for this purpose, but a good proposal weighs all alternatives.

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [April 17, 2024, 3:30pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/7 "2024-04-17T15:30:37Z")

</div>

I definitely am not inclined to introduce new syntax for this purpose, but I do like the API form. I've actually [suggested the same thing elsewhere](https://matrixlogs.bakkot.com/TC39_Delegates/2023-11-29#L10), though I won't have time to champion this in the immediate future.

On the return type, I think `{ success: true, value } | { success: false, error }` is the right API shape. Compare [Promise.allSettled](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled#return_value), which returns `{ status: 'fulfilled', value } | { status: 'rejected', reason }` - those names are very promise-specific (and bad), but otherwise it's the same sort of thing.

On the question of handling methods, you could also have a `tryCall` that takes as its first argument the `this`, like `call`, so like `let res = o.m.tryCall(o, x)`.

---

<div class="post-metadata">

**Author:** ![helloworld228](https://avatars.discourse-cdn.com/v4/letter/h/e79b87/32.png) [@helloworld228](https://es.discourse.group/u/helloworld228)\
**Post date:** [April 17, 2024, 4:06pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/8 "2024-04-17T16:06:28Z")

</div>

Hey @bakkot thank you for the feedback! It's nice to see that there is a Delegate who is interested in this proposal (even though you don't have time to push it through).

I like the `Function.prototype.tryCall` idea to accommodate for method calls.

The `Result` object shape you're proposing is only different in `success` field instead of `isError` and I think I can agree that it would be nicer to have the name without capitalization.  
On the other side (_this is just a personal preference and I would not fight for it_) – I'm not a big fan of `success` because in order to check for the error you would need to negate it e.g.:

```js
const { success } = JSON.parse.try("");
if (!success) {
    // handle error
}

```

Perhaps, `catched` could be an alternative name? Either way, naming is hard, so I think we should not focus on it too much just yet.

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [April 17, 2024, 4:38pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/9 "2024-04-17T16:38:00Z")

</div>

"threw" is already kind of a negation. Returned booleans should correspond to the positive condition, not the negative one. That's why we have `Set.prototype.has` and not `Set.prototype.absent`, for example. So regardless of the name, the boolean `true` should indicate "did not error".

---

<div class="post-metadata">

**Author:** ![helloworld228](https://avatars.discourse-cdn.com/v4/letter/h/e79b87/32.png) [@helloworld228](https://es.discourse.group/u/helloworld228)\
**Post date:** [April 17, 2024, 4:47pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/10 "2024-04-17T16:47:55Z")

</div>

> [@bakkot](#):
>
> So regardless of the name, the boolean `true` should indicate "did not error"

I could live with that in the world where this API would get implemented in JavaScript.

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [April 23, 2024, 11:01pm UTC](https://es.discourse.group/t/function-prototype-try-for-error-handling/2016/11 "2024-04-23T23:01:41Z")

</div>

Will note that a `try expr` syntax also allows for `try await` and `try yield`, unlike a method form. And `try await` would encapsulate _many_ of my uses. Syntax here is strictly and significantly more expressive than a method.
