DEV Community

Cover image for Bye Bye, Try-Catch Blocks: Meet JavaScript's Safe Assignment Operator Proposal😉
Dharmendra Kumar
Dharmendra Kumar

Posted on

Bye Bye, Try-Catch Blocks: Meet JavaScript's Safe Assignment Operator Proposal😉

Introduction

JavaScript error handling is about to get a major upgrade. The new ECMAScript Safe Assignment Operator Proposal (?=) is here to streamline your code by reducing the need for traditional try-catch blocks. Let’s explore how this proposal can simplify your error management and make your JavaScript code cleaner and more efficient.

Simplified Error Handling

No More Nested Try-Catch

  • Problem: Traditional try-catch blocks often lead to deeply nested code, making it harder to read and maintain.
  • Solution: The ?= operator reduces nesting by transforming the result of a function into a tuple. If an error occurs, it returns [error, null]; otherwise, it returns [null, result].

Example:

   async function getData() {
     const [error, response] ?= await fetch("https://api.example.com/data");
     if (error) return handleError(error);
     return response;
   }
Enter fullscreen mode Exit fullscreen mode

Enhanced Readability

Cleaner, More Linear Code

  • Problem: Try-catch blocks can clutter code and disrupt the flow of logic.
  • Solution: The ?= operator makes error handling more intuitive, keeping your code linear and easy to follow.

Example:

   const [error, data] ?= await someAsyncFunction();
   if (error) handle(error);
Enter fullscreen mode Exit fullscreen mode

Consistency Across APIs

Uniform Error Handling

  • Problem: Different APIs might require different error-handling techniques, leading to inconsistencies.
  • Solution: The ?= operator introduces a consistent way to handle errors across all APIs, ensuring uniform behavior.

Improved Security

Never Miss an Error Again

  • Problem: Overlooking error handling can lead to unnoticed bugs and potential security risks.
  • Solution: By automatically handling errors in a standardized way, the ?= operator reduces the chance of missing critical errors.

Symbol.result: The Secret Sauce

Customizable Error Handling

  • Overview: Objects that implement the Symbol.result method can use the ?= operator to define their own error-handling logic.
  • Usage: The Symbol.result method should return a tuple [error, result].

Example:

   function example() {
     return {
       [Symbol.result]() {
         return [new Error("Error message"), null];
       },
     };
   }
   const [error, result] ?= example();
Enter fullscreen mode Exit fullscreen mode

Recursive Error Handling

Handle Nested Errors Like a Pro

  • Overview: The ?= operator can recursively handle nested objects that implement Symbol.result, ensuring even complex error scenarios are managed smoothly.

Example:

   const obj = {
     [Symbol.result]() {
       return [
         null,
         { [Symbol.result]: () => [new Error("Nested error"), null] }
       ];
     },
   };
   const [error, data] ?= obj;
Enter fullscreen mode Exit fullscreen mode

Promises and Async Functions

Async Error Handling Made Easy

  • Overview: The ?= operator is designed to work seamlessly with Promises and async/await, making error handling in asynchronous code straightforward.

Example:

   const [error, data] ?= await fetch("https://api.example.com");
Enter fullscreen mode Exit fullscreen mode

Using Statement Integration

Streamline Resource Management

  • Overview: The ?= operator can be used with using statements to manage resources more effectively, making cleanup easier and less error-prone.

Example:

   await using [error, resource] ?= getResource();
Enter fullscreen mode Exit fullscreen mode

Why Not Data First?

Prioritizing Error Handling

  • Overview: Placing the error first in the [error, data] ?= structure ensures that errors are handled before processing data, reducing the risk of ignoring errors.

Example:

   const [error, data] ?= someFunction();
Enter fullscreen mode Exit fullscreen mode

Polyfilling the Operator

Future-Proof Your Code

  • Overview: While the ?= operator cannot be polyfilled directly, its behavior can be simulated using post-processors to maintain compatibility with older environments.

Example:

   const [error, data] = someFunction[Symbol.result]();
Enter fullscreen mode Exit fullscreen mode

Learning from Other Languages

Inspired by the Best

  • Overview: The pattern behind the ?= operator is inspired by similar constructs in languages like Go, Rust, and Swift, which have long embraced more structured error handling.

Current Limitations and Areas for Improvement

Still a Work in Progress

  • Nomenclature: The proposal needs a clear term for objects implementing Symbol.result.
  • Finally Blocks: There’s no new syntax for finally blocks, but you can still use them in the traditional way.

For more information, visit the GitHub repository.

Conclusion

The Safe Assignment Operator (?=) is a game-changer for JavaScript error handling, promising to reduce the need for clunky try-catch blocks and make your code cleaner and more secure. Although still in development, this proposal could soon become a standard tool in every JavaScript developer’s toolkit.

Top comments (48)

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

It won't be soon (if at all) - this is only a draft proposal that hasn't even been accepted for consideration yet, let alone adoption

Collapse
 
leob profile image
leob • Edited

Lol the "elephant in the room" - the article does not mention anything about where this proposal is documented, nor about its expected timeline ...

"Bye Bye, Try-Catch Blocks" - that's highly premature, to put it mildly ...

"JavaScript error handling is about to get a major upgrade" - is rather misleading ...

P.S. and if you think critically about it for a moment, then you'll probably conclude that the advertised advantages might actually be disadvantages in the context of JS ... I see more negative views of this proposal than positive ones here:

reddit.com/r/javascript/comments/1...

I'm also not really convinced ...

Collapse
 
link2twenty profile image
Andrew Bone • Edited

If you look at the syntax votes ?= is not even the front runner.

pie chart showing 'try as throw' in the lead

(also total votes add up to 666)

Collapse
 
crazytonyi profile image
Anthony

Can you link the details of the above? Wondering what try as throw means.

Thread Thread
 
link2twenty profile image
Andrew Bone
Thread Thread
 
crazytonyi profile image
Anthony

Thanks! I love the lively discussion going on over there.

Collapse
 
doctorew profile image
Drew Schillinger

Dang. I'm looking forward to the 101 almost-the-same-but-missing-nuanced implementations that npm packages or each framework will introduce...

Collapse
 
link2twenty profile image
Andrew Bone

If you're really adverse to the try/catch block you can make a helper function that does the same as the safe assignment operator. I really don't see it getting much traction unfortunately, especially seeing as it's such an easy function.

/**
 * execute a callback and return an result/error array
 *
 * @param {callback} function to be executed
*/
const safePromise = async (callback) => {
  try {
    const res = await callback();

    return [null, res]
  } catch (error) {
    return [error, null];
  }
}

// Example use
const [error, data] = await safePromise(() => fetch("https://api.example.com"));
if (error) handle(error);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
blobkat profile image
BlobKat

You don't need a callback, this can be done by simply awaiting the value (Promise object)

Collapse
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

What you don't need is precisely await. Here's an example of this:

/**
 * Fetch implementation details, you could do it in any different way that's suitable to your project
 */
const fetcher = async (config: ServiceConfiguration): Promise<ServiceError | ServiceResponse> => {
  try {
    return fetch(config.target, { method: config.operation, headers: config.headers }).then((res) => res.json());
  } catch (error: RequestError) {
    return { svcError: error, ...config } as ServiceError;
  }
};

/**
 * This doesn't even need to be a "hook" it would work with a different name being a normal function.
 */
export const useService = async (config: ServiceOperation, setter?: Dispatch<SetStateAction<ServiceResponse>>) => {
  fetcher(config).then((res) => {
    if (res?.hasOwnProperty('svcError')) return Promise.reject(res);
    else if (setter) setter(res);
    else return Promise.resolve(res);
  });
};
Enter fullscreen mode Exit fullscreen mode

This works pretty well when loading multiple stuff at the same time so you don't block the thread on every single request by awaiting the response before continuing with the next one; meaning that you can call useService in a promise.all or promise.allSettled and expect all requests to be launched at the same time, in parallel and capture the responses seamlessly (and asynchronously as -hopefully- intended).

This can be used along any framework, library or with Vanilla JS if you are eager to toy around with object proxies or any other methodology you prefer. This is, though, a simplification of what I have on a Next (React) project as you may have noticed by the Dispatch type hint. Following that, an usage example would be:

const [shoppingHistory, setShoppingHistory] = useState();

useEffect( () => {
  const config: ServiceOperation = getServiceConfig(); // implementation details

  useService(config, setShoppingHistory);
}, [userId]);

useEffect(()=>{
  console.log('shopping history has been loaded');
}, [shoppingHistory]);
Enter fullscreen mode Exit fullscreen mode

We're coding in a "reactive" manner pretty much since Angular JS first came out (14 years ago 🤯), try to avoid breaking the asynchrony unless it's completely necessary; instead let things react to other things when they find suit, simply let it flow! 😎

As a bonus advice, try not to use await in the same instruction where a then is present and vice-versa unless you know very well what you are doing.

Image description

Collapse
 
oculus42 profile image
Samuel Rouse • Edited

This is what the title example looks like with promises.

function getData() {
  return fetch("https://api.example.com/data").catch(handleError);
}
Enter fullscreen mode Exit fullscreen mode

The proposal reminds me of the "nodeback" error-first callback style before the majority of the asynchronous actions adopted promises. I prefer promises, and dislike the need for try/catch blocks and async function declarations that come with await. This proposal is a better solution than try/catch blocks, but it seems to me like we're 60% of the way back to a promise chain but still with more characters and more mental parsing.

someFunction().then().catch().finally();

Also, there is no need for a finally equivalent with Safe Assignment because we've eliminated the scoping. You can use plain conditions.

if (error) {
  // .catch() equivalent
} else {
  // .then() equivalent
}
// .finally() equivalent
Enter fullscreen mode Exit fullscreen mode

Thank you for putting the article together. I appreciate the work on it, even if I don't like proposal. 😅

Collapse
 
saad_zahem profile image
saad zahem

I agree with you but there is one thing though. finally block is meant to be executed regardless of whether your code succeed, get an error and handle it successfully, get an error and throw it again, or throw its own error. It is a misconception that a finally block is equivalent to the code that follows the try-catch-else block.

if (error) {
  // .catch() equivalent
} else {
  // .then() equivalent
  throw Error();
}
// This will not be executed like a finally block
Enter fullscreen mode Exit fullscreen mode

Instead, I suggest this modified solution.

if (error) {
  // .catch() equivalent
  var errorToThrow = null;
} else {
  // .then() equivalent
  var errorToThrow = Error();
}
// .finally() equivalent, executed in all cases
if (errorToThrow !== null) throw errorToThrow;
Enter fullscreen mode Exit fullscreen mode

Finally, finally block is used to free allocated resources or to close a file so we make sure that it is not interrupted by any event.

Collapse
 
devto2k5 profile image
dev procedure
Problem: Try-catch blocks can clutter code and disrupt the flow of logic.
Solution: The ?= operator makes error handling more intuitive, keeping your code linear and easy to follow.
Enter fullscreen mode Exit fullscreen mode

100% FALSE and B.S.
Try-Catch is straight-forward language.
The ?= is CONFUSI(NG to begin with.

The ?= should be canned and thrown away.
It's look stupid and Go, Rust, and Swift, are not known to easy or that popular.
BUt lots of HYPE...

The ?= is just WANNBE nonsense.

Collapse
 
rafageist profile image
Rafa Rodríguez

It's still a draft. In the meantime you can do this:

const attempt = (operation) => {
   let result = null;
   let error = null;

   try {
      result = operation(); 
   } catch(err) {
      error = err;
   }

   return [error, result];
};

const attemptAsync = async (operation) => {
   let result = null;
   let error = null;

   try {
      result = await operation();
   } catch(err) {
      error = err;
   }

   return [error, result];
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jexroid profile image
Amirreza Farzan (jexroid) • Edited

it seems like javascript is learning something from GO:

file, err := os.Create(filepath.Join(projectPath, ".air.toml"))
    if err != nil {
        return err
    }
Enter fullscreen mode Exit fullscreen mode

golang doesn't support try-catch blocks at all.

Collapse
 
kbbdy profile image
Krzysztof • Edited

What about this:

const result = await myfunction()
    .catch(()=>{ 
       // handle error
    })
Enter fullscreen mode Exit fullscreen mode
Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

So basically, JavaScript has found yet another way of being "kinda like Lua, except worse"?

Collapse
 
yeasin2002 profile image
Md Kawsar Islam Yeasin

It's like null, error block of GO

Collapse
 
tamjid_ahmed_fdd5b20eef0c profile image
Tamjid Ahmed

i n d i a n c l i c k b a i t.

Collapse
 
richard809 profile image
Richard Holguín

From the producers of unnecessary copy/pasted Medium post, comes...

Collapse
 
techninjax profile image
Techninjax

hmmm will i say this is better or harder

Collapse
 
freelancer2020 profile image
Mostafa

Generally handling errors in software a critical topic and I don't think what you just shared with us will be a part of Ecma

Collapse
 
syedmuhammadaliraza profile image
Syed Muhammad Ali Raza

Image description

Collapse
 
cutmastakun profile image
cutmasta-kun

Yes, please! :3 I love this style of error handling in go.

Collapse
 
ankit_rattan profile image
Ankit Rattan

Well! JS is now competing with Python in short syntax! 😊😁

Some comments may only be visible to logged-in visitors. Sign in to view all comments.