· Zen HuiFer · Learn  · 4 min read

A New JavaScript Proposal to Say Goodbye to Try Catch!

Discover the new JavaScript proposal for a safer assignment operator that aims to streamline error handling. Say goodbye to try-catch with ?=, enhancing code security and simplicity.

Discover the new JavaScript proposal for a safer assignment operator that aims to streamline error handling. Say goodbye to try-catch with ?=, enhancing code security and simplicity.

Error handling has always been an important but complex issue in modern web development. conventional try-catch Although statements are powerful, they can easily lead to lengthy and difficult to maintain code.

To simplify this process,ECMAScriptA new proposal has recently been introduced:proposal-safe-assignment-operatorThe ‘Safe Assignment Operator’ is denoted as?=)。

Summary of Proposal

Secure assignment operator?=The goal is to simplify error handling.

It handles errors by converting the result of a function into an array.

  • If the function throws an error, the operator returns[error, null]

  • If the function is successfully executed, return[null, result]

This operator andPromise、asyncFunctions and any implementationSymbol.resultThe values of the method are compatible.

For example, when executingI/OOperation or based onPromiseDuring API interaction, unexpected errors may occur at runtime.

If these errors are ignored, it may lead to unexpected behavior and potential security vulnerabilities. Using secure assignment operators can effectively handle these errors:

const [error, response] ?= await fetch("https://blog.conardli.top");

Motivation for Proposal

  1. Simplify error handling : By eliminatingtry-catchBlock, simplify error management process;

  2. Enhance code readability Reduce nesting, improve code clarity, and make the error handling process more intuitive;

  3. Cross API consistency Establish a unified error handling method across different APIs to ensure behavioral consistency;

  4. Improve security Reduce the risk of ignoring error handling, thereby enhancing overall code security.

Example usage

The following is a typical example of not using?=Example of error handling for operators:

async function getData() {
  const response = await fetch("https://blog.conardli.top");
  const json = await response.json();
  return validationSchema.parse(json);
}

The above function has multiple points that may have anomalies (e.gfetch()、json()、parse())We can use?=Operators are processed in a very concise and easy to read manner:

async function getData() {
  const [requestError, response] ?= await fetch("https://blog.conardli.top");  if (requestError) {
    handleRequestError(requestError);
    return;
  }  const [parseError, json] ?= await response.json();  if (parseError) {
    handleParseError(parseError);
    return;
  }  const [validationError, data] ?= validationSchema.parse(json);  if (validationError) {
    handleValidationError(validationError);
    return;
  }  return data;
}

Proposal function

Symbol.result

Any implementation has been madeSymbol.resultThe objects of the method can all be related to?=Use operators together.

Symbol.resultThe method must return an array, where the first element represents the error and the second element represents the result.

function example() {
  return {
    [Symbol.result]() {
      return [new Error(    An error has been reported    ), null]
    },
  }
}const [error, result] ?= example() // Function.prototype also implements Symbol.result
// const [error, result] = example[Symbol.result]()// error is Error('123')

Safe assignment operator (?=)

?=The operator calls the object or function on the right side of the operatorSymbol.resultMethod to ensure consistent handling of errors and results in a structured manner.

const obj = {
  [Symbol.result]() {
    return [new Error("Error"), null]
  },
}const [error, data] ?= obj
// const [error, data] = obj[Symbol.result]()
function action() {
  return 'data'
}const [error, data] ?= action(argument)
// const [error, data] = action[Symbol.result](argument)

The results should comply with[error, null | undefined]or[null, data]The format.

When used in a function?=When using an operator, all parameters passed to the function will be forwarded toSymbol.resultmethod.

declare function action(argument: string): stringconst [error, data] ?= action(argument1, argument2, ...)
// const [error, data] = action[Symbol.result](argument, argument2, ...)

When?=When used with an object, operators do not pass any parameters toSymbol.resultmethod.

declare const obj: { [Symbol.result]: () => any }const [error, data] ?= obj
// const [error, data] = obj[Symbol.result]()

Recursive processing mechanism

in use[error, null]When encountering an array, the first exception will be generated. However, if[null, data]The data in the array has also been implementedSymbol.resultMethod, then the method will be recursively called.

const obj = {
  [Symbol.result]() {
    return [
      null,
      {
        [Symbol.result]() {
          return [new Error("Error"), null]
        },
      },
    ]
  },
}const [error, data] ?= obj
// const [error, data] = obj[Symbol.result]()     //Error is Error ('string ')     

This behavior helps to handle various situations that includeSymbol.resultMethods ofPromiseOr object:

  • async function(): Promise<T>

  • function(): T

  • function(): T | Promise<T>

Handling Promise

PromiseExceptFunctionBesides, the only way to?=The implementation of using operators together.

const promise = getPromise()
const [error, data] ?= await promise
// const [error, data] = await promise[Symbol.result]()

You may have already noticedawaitand?=Can be used together and absolutely no problem. Due to their recursive processing characteristics, they can be well combined together.

const [error, data] ?= await getPromise()
// const [error, data] = await getPromise[Symbol.result]()

The execution sequence is as follows:

  1. getPromise[Symbol.result]()When called, an error may be thrown (if it is a synchronous function that returns a Promise).

  2. If an error is thrown, it will be assigned toerrorAnd the execution will stop.

  3. If no errors are thrown, the result will be assigned todata. becausedataIt is a promise, and the promise hasSymbol.resultMethod, so it will be recursively processed.

  4. If the Promise is rejected, the error will be assigned toerrorAnd the execution will stop.

  5. If the Promise is resolved, the result will be assigned todata

Through this recursive processing mechanism, you can simplify the handling of various complex nested objects and promises, making the code more concise and easy to read.

Polifll

This proposal is still in its early stages and it will take a long time to enter the standard. Currently, it is necessary to use thispolifill

https://github.com/arthurfiorette/proposal-safe-assignment-operator/blob/main/polyfill.js

However,?=The operator itself cannot be directly appliedpolyfill. When targeting older onesJavaScriptIn the environment, a compiler is required to?=Convert operators to their corresponding[Symbol.result]Call.

const [error, data] ?= await asyncAction(arg1, arg2)
// should become
const [error, data] = await asyncAction[Symbol.result](arg1, arg2)const [error, data] ?= action()
// should become
const [error, data] = action[Symbol.result]()const [error, data] ?= obj
// should become
const [error, data] = obj[Symbol.result]()

Proposal address: https://github.com/arthurfiorette/proposal-safe-assignment-operator

Back to Blog

Related Posts

View All Posts »
New package in Go 1.23: unique

New package in Go 1.23: unique

Go 1.23 introduces unique package for value normalization, enhancing memory efficiency and equality checks. Learn how "interning" works with unique and its benefits for Go developers.

How to cache well in Go

How to cache well in Go

Optimize Go app performance with caching strategies. Learn local vs distributed cache, memory management, and eviction policies. Enhance efficiency with Go's new unique package.

The noCopy strategy you should know in Golang

The noCopy strategy you should know in Golang

Discover the importance of noCopy in Golang development. Learn how it prevents accidental copying of critical structures like sync primitives. Enhance your Go code safety and efficiency.