Enhanced Try Syntax

Hi everyone,

Thank you all for your valuable feedback and for engaging in this discussion.

Getting Attention from TC39 Members

I'm eager to move this proposal forward within the TC39 process and would appreciate any guidance on how to get the attention of TC39 members who might be interested in championing it. If anyone has suggestions or can facilitate an introduction to potential champions, I would be grateful.

Prevalence of the Pattern

I want to emphasize that the pattern of intentionally ignoring errors is widespread in JavaScript codebases. There are many scenarios where a thrown error is not critical, and developers choose to proceed without handling it explicitly. Providing a concise syntax for these cases can improve code readability and reduce boilerplate.

Additional Use Cases:

  • Non-Critical API Calls:

    Sometimes, we might call an API where the success is optional, and failure should not interrupt the flow.

    try {
      optionalFeature.enable();
    }
    // Proceed regardless of whether the feature was enabled
    
  • Best-Effort Operations:

    Operations that are attempted but not essential for the main logic.

    try {
      cache.set(key, value);
    }
    // Continue even if caching fails
    
  • Cleanup Tasks:

    Executing cleanup code where failure to clean up doesn't impact the main process.

    try {
      temporaryFiles.forEach(file => fs.unlinkSync(file));
    }
    // Proceed even if some files couldn't be deleted
    
  • Resource Release:

    Attempting to release resources that may already be released.

    try {
      connection.close();
    }
    // Ignore errors if connection was already closed
    

Examples from Other Languages

This concept is not unique to JavaScript; several other languages provide mechanisms for succinct error handling when the error can be safely ignored.

  • C#:

    The try block can be used without a catch or finally when dealing with asynchronous methods.

    try
    {
        await SomeNonCriticalOperationAsync();
    }
    // No catch or finally block
    
  • PHP:

    The @ operator suppresses errors for a particular expression.

    $data = @file_get_contents($url);
    // Suppresses any warnings or errors
    

Addressing Concerns About Bad Practices

I understand the concerns about potentially encouraging bad practices by making it easier to ignore errors. However, developers are already employing these patterns extensively. By providing a concise and intentional syntax, we can make the code's intent clearer.

  • Intentional Ignoring: Using try without catch makes it explicit that any errors are being deliberately ignored, as opposed to an accidental oversight.

  • Improved Readability: Reducing boilerplate code allows developers to focus on the main logic, improving overall code readability.

  • Tooling Support: Linters and code analysis tools can be configured to flag inappropriate use of empty try blocks, helping maintain code quality.

Next Steps

  1. Seeking a Champion:

    If anyone here is a TC39 member or can connect me with someone interested in championing this proposal, please let me know. Your assistance would be greatly appreciated.

  2. Gathering More Feedback:

    I welcome further feedback and examples. If you have additional use cases, concerns, or suggestions, please share them so I can address them in the proposal.

Thank you again all for your time and insights

2 Likes

Hi!

My personal feeeback remains the same as my previous comment.

Syntax changes can have a high cost, they require that teams ensure all the tools they use can handle the new syntax. e.g. Bundlers, minifiers, formatters, linters, code mods.

2 Likes

I understand that this is going to cost a bunch of work for everyone involved to land. But as a user of JavaScript I would be happy to have this in the language.
Just today I wrote this code:

let response: Response | null = null;
try {
response = await route.request().response();
} catch {
// ignore trying to get response of the requests that are not successful
}

This could be written as

const response = try await route.request().response();

A ton of boilerplate, including the necessity to import and use types (Response) will be removed if this syntax is added to JavaScript.

2 Likes

I believe you can do const response = await route.request().response().catch(() => null); already?

3 Likes

Why not making best of two worlds? I considered writing my own proposal for it, but since this proposal is pretty close, I wanted rather to see if you agree about the proposed fix.

const [err, data] = try JSON.parse(input);

// you can still ignore any errors easily, but it is much more explicit
const [, data] = try JSON.parse(input);

// similar for promise-like return values
const [err, response] = try await doRequest();

I wouldn't touch the "try" block by itself, as I believe that error handling should be done rather more explicitly, but I agree that the suggested syntax would increase the overall readability of the code.

Hence, this syntax change would improve the language.

I think it will be easier to get this proposal into JS. Would you agree?

3 Likes

I like the proposal but I’m a bit worried about “making easier to write good code” also brings “making easier to write bad code”.

I would encapsulate try-catch (with empty catch) inside util functions that make you happy using them. In fact I could find error ignoring useful with some existing bad API designs. But I’m not sure the language should make easy to ignore errors so silently.

With that in mind, maybe having many empty catch () {} in your code helps you to improve how you manage errors in your projects. It’s usually a code smell rather than a bad language design.

I personally have given up on this proposal. There is no appetite to adopt such idea it seems. Not clear to me what makes JavaScript different than other languages that have the exact same feature though…

What about GitHub - arthurfiorette/proposal-try-expressions: Draft for ECMAScript Error Safe Assignment Operator? It's the improved versionofm the past proposal-safe-assignment-operator and has almost all major concerns (as far as I know) solved. It might not have the best ergonomics, although this is more of a personal preference than anything else, but works.

I really like how you identified the core problem with try; I've never looked at the language construct in separate parts like that. Beyond that I hope your proposal goes far.

Something that you may want to specify as a non-goal is the methods for Result. If you let methods to chain computation off results, then you will get a large amount of scope creep and bike-shedding about names and semantics.

If you add it as a non-goal to specify methods for Result then those can be discussed in a second proposal once the (syntax) primitive is in.

1 Like

In this proposal, what is the difference between try something() and tryDoing(() => something()) where tryDoing might be:

function tryDoing(task) {
  try { return task() } catch (_ignored) {}
}

By comparison, here is tryDoing such that it matches try something() from the proposal:

function tryDoing(task) {
  try {
    return [true, undefined, task()];
  } catch (error) {
    return [false, error, undefined];
  }
}
// Note: The full Result class is omitted for brevity, focussing on the iterable-destructured assignment pattern instead.
const [ok, error, value] = tryDoing(something);

Differences:

  1. Thrown errors are not ignored, instead captured and returned.
  2. Besides the value and error being returned separately, a boolean marking the completion scenario.

It seems adoption of the community and dev influencers for the safe assignment are growing. There are already plenty different implementations available for wrapper functions trying to achieve something close.

It makes the code actually more readable. Especially, with a project setup using Go+TS. Making both more look alike.

This is the closest I could get with the following goals and no syntax adoption:

  • minimalistic
  • type-safe
  • explicit

-> @backend/safe-assignment - JSR

Hi gang! That's a great idea!!!
I was about to ask for TRY without CACHE, but thankfully I checked first if there is already something on the topic :)

Very often I need just the TRY, but I am required to have an empty cache, you know how it is :)

Also the idea for inline TRY is absolutely awesome!
To return undefined to be able to use directly like in the example of "mohsen1"
Yup, I want that too :sweat_smile:
let data = try JSON.parse(input) ?? {};