DEV Community

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

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

Dharmendra Kumar on August 20, 2024

Introduction JavaScript error handling is about to get a major upgrade. The new ECMAScript Safe Assignment Operator Proposal (?=) is her...
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
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

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

Collapse
 
kbbdy profile image
Krzysztof • Edited

What about this:

const result = await myfunction()
    .catch(()=>{ 
       // handle error
    })
Enter fullscreen mode Exit fullscreen mode
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
 
yeasin2002 profile image
Md Kawsar Islam Yeasin

It's like null, error block of GO

Collapse
 
sakib412 profile image
Najmus Sakib

Misleading title

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
 
ankit_rattan profile image
Ankit Rattan

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

Collapse
 
cutmastakun profile image
cutmasta-kun

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

Collapse
 
akashkava profile image
Akash Kava

If accepted, this will include nightmare in debugging, in an explicit try-catch, author and maintaner both are aware of what is happening, aim of developer is not to write short code, but maintanable code.

If I am looking at my own code after one year, I would usually search for try keyword and investigate on it. Put a breakpoint and see what error is thrown and why. I don't think there should be any proposal which would consume an exception and return something else.

Collapse
 
daelmaak profile image
Daniel Macák

The title is super misleading since it makes it sound like this feature is like ECMA Stage 3 which is totally untrue. Claiming "this proposal could soon become a standard tool in every JavaScript developer’s toolkit" is very naive; just remember how much it took for Decorators to come to Stage 3, it's been an eternity.

As for the proposal itself, yes it improves error handling in some areas but to me it looks like it makes other areas worse, like bubbling up errors would become way more manual with it.

Collapse
 
intrnl profile image
intrnl • Edited

This is awful, I'd much prefer do expressions to come around, which would set up a precedence for try expressions

const messages = try {
  await loadMessages(lang);
}  catch (err) {
  reportError(err);
  await loadMessages("en");
}
Enter fullscreen mode Exit fullscreen mode

This is a lot better than replicating Go's semantics.

Collapse
 
zorqie profile image
I, Pavlov

Does anyone really think that this

const obj = {
     [Symbol.result]() {
       return [
         null,
         { [Symbol.result]: () => [new Error("Nested error"), null] }
       ];
     },
   };

Enter fullscreen mode Exit fullscreen mode

is better than nested try-catch?

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

What prevents a developer from doing this:

const [, data] ?= someFunction();
useDataIgnoringError(data);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sschneiderihrepvs profile image
sschneider-ihre-pvs

You can today use a similar construct without the operator of course since js does not allow custom operators or operator overloading except in a compiler like svelte $: for example.
The concept really resembles the Either Monad and libs like effect.website/ really push this in a good way

Collapse
 
peter-fencer profile image
Peter Vivo • Edited

Until ?= operator did not pass the proposal state, this is just a fairy tale. I know good to play with proposal things, sometimes it is really worth. My favorite is the pipeline operator |> which is really missing from JS because give natural solution to chaining any one parameter function.

Collapse
 
alaindet profile image
Alain D'Ettorre

Although this is very far from being implemented, I believe it could be worth it. The main problem I see is that Go enforces you to handle errors like this, the whole standard library returns multiple values with error being the second value (usually), people are encouraged to do the same and the syntax allows you to return multiple values, also no exceptions that "bubble" exist.

JS, instead, relies on 20+ years of Exception-based programming and having an alternative like this seems a good way to fragment code once again

Collapse
 
jmezzacappa profile image
Joey Mezzacappa

That example doesn't work if error is falsy.

Collapse
 
christianpaez profile image
Christian Paez • Edited

Interesting proposal, however I still prefer standard try catch blocks, to me const [error, response] seems ugly, no reason to declare those as parts of an array.

Collapse
 
guille profile image
Guillermo Liss

I personally prefer this way to the try-catch. I hope eventually this becomes part of js.

Collapse
 
zain64design profile image
Collapse
 
danielo515 profile image
Daniel Rodríguez Rivero

Awful. JS should find its way, rather than copying the bad implementation of good ideas (better error handling is needed but the way golang does it is awful)

Collapse
 
martinbaun profile image
Martin Baun

Reminds me of await-to-js, which is pretty neat in my opinion.

Collapse
 
albasyir profile image
Abdul Aziz Al Basyir

i used to

const result = await fetch().catch(e => {});

console.log(result)

Collapse
 
progkidscom profile image
ProgKids

Awesome post!

Collapse
 
devto2k5 profile image
dev procedure

It's a stupid proposal as ?= is confusing and Try-Catch is very straight-forward and WORKS

Collapse
 
slex1onemusdy profile image
Mustafa • Edited

Right?? Since the Try-Catch structure is a common way of catching errors in programming, it also improves code readability for others who read your code.

Absolutely no need for fancy things.

Collapse
 
lewisdonovan profile image
Lewis Donovan

This is never getting anywhere near ECMA

Collapse
 
liemle3893 profile image
Liem Le Hoang Duc • Edited

So, I'm trying to say that go error handling is such, and now seeing this on JS? Hope this proposal never get approved. Try/Catch/Finally is not evil or bad. and if (error) is such.

Collapse
 
claem profile image
Castric Laem

This is obviously written by ChatGPT. The whole article screams prompt engineering and a poor attempt at making this readable. Which means the author has no idea what half of this article means.

Collapse
 
gitantonyuk profile image
Alexander Antonyuk • Edited

Sweet!

I like this approach, like in Golang.

Collapse
 
yogithesymbian profile image
Yogi Arif Widodo

can you make a real example condition for example notes app maybe :D

Collapse
 
katarzyna_krawczyk_d2f8c1 profile image
Katarzyna Krawczyk • Edited

Looks great <3 !

Collapse
 
ashikur_asif_c4b917c39e36 profile image
Ashikur Asif • Edited

ট্রাই ক্যাচ ব্লক কি তুমার নুনুতে কামড় দিছে নাকি?