专栏名称: 前端大全
分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯
目录
相关文章推荐
启四说  ·  启四VIP策略网站,有哪些功能?如何使用? ·  14 小时前  
启四说  ·  启四VIP策略网站,有哪些功能?如何使用? ·  14 小时前  
前端早读课  ·  【第3451期】前端 TypeError ... ·  昨天  
江苏司法行政在线  ·  宿迁司法行政人、江苏监狱戒毒民警,给您拜年啦! ·  3 天前  
江苏司法行政在线  ·  宿迁司法行政人、江苏监狱戒毒民警,给您拜年啦! ·  3 天前  
51好读  ›  专栏  ›  前端大全

新的 JS 提案让你告别 try catch !

前端大全  · 公众号  · 前端  · 2024-09-13 11:50

主要观点总结

文章介绍了现代Web开发中错误处理的重要性及复杂性,并引出ECMAScript近期引入的新提案Safe Assignment Operator(安全赋值运算符)(记作?=)。该提案旨在简化错误处理流程,通过消除try-catch块,减少嵌套,提高代码清晰度和可读性,并在不同的API中建立统一的错误处理方法。文章详细描述了提案的概要、动机、功能、示例和递归处理机制等。

关键观点总结

关键观点1: 提案目的

简化错误处理流程,提高代码可读性和安全性。

关键观点2: 主要特点

使用安全赋值运算符可以有效地处理各种可能的错误,使代码更简洁易读。

关键观点3: 使用方法

使用示例展示了如何使用安全赋值运算符处理可能抛出异常的点。

关键观点4: 递归处理机制

当处理包含Symbol.result方法的对象或Promise时,安全赋值运算符的递归处理机制有助于简化处理流程。

关键观点5: 限制和注意事项

提案还处于初期阶段,需要较长时间才能进入标准。当下使用时,需要使用编译器将安全赋值运算符转换为相应的[Symbol.result]调用。


正文

在现代 Web 开发中,错误处理一直是个重要但复杂的问题。传统的 try-catch 语句虽然功能强大,但是容易导致代码冗长且难以维护。

为了简化这一过程, ECMAScript 近期引入了一个新的提案: proposal-safe-assignment-operator ,“安全赋值运算符”(Safe Assignment Operator,记作 ?= )。

提案概要

安全赋值运算符 ?= 的目标就是简化错误处理。

它通过将函数的结果转换为一个数组来处理错误。

  • 如果函数抛出错误,则运算符返回 [error, null]
  • 如果函数成功执行,则返回 [null, result]

这一运算符与 Promise、async 函数以及任何实现了 Symbol.result 方法的值兼容。

例如,当执行 I/O 操作或与基于 Promise 的 API 交互时,运行时可能会出现意外错误。

如果忽略了这些错误,可能会导致意外的行为和潜在的安全漏洞。使用安全赋值运算符可以有效地处理这些错误:

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

提案动机

  1. 简化错误处理 :通过消除 try-catch 块,简化错误管理流程;
  2. 增强代码可读性 :减少嵌套,提高代码的清晰度,使错误处理的流程更直观;
  3. 跨API一致性 :在不同的 API 中建立统一的错误处理方法,确保行为一致性;
  4. 提高安全性 :减少忽略错误处理的风险,从而增强代码整体安全性。

使用示例

以下是一个典型的不使用 ?= 运算符的错误处理示例:

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

上述函数存在多个可能存在异常的点(例如 fetch()、json()、parse() ),我们可以使用 ?= 运算符进行非常简洁、易读的处理:

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;
}

提案功能

Symbol.result

任何实现了 Symbol.result 方法的对象都可以与 ?= 运算符一起使用。

Symbol.result 方法必须返回一个数组,其中第一个元素表示错误,第二个元素表示结果。

function example({
  return {
    [Symbol.result]() {
      return [new Error("报错啦!"), null]
    },
  }
}

const [error, result] ?= example() // Function.prototype also implements Symbol.result
// const [error, result] = example[Symbol.result]()

// error is Error('123')

安全赋值运算符 (?=)

?= 运算符调用运算符右侧对象或函数上的 Symbol.result 方法,确保以结构化方式一致地处理错误和结果。

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)

结果应符合 [error, null | undefined] [null, data] 的格式。

当在函数中使用 ?= 运算符时,传递给该函数的所有参数都将转发给 Symbol.result 方法。

declare function action(argument: string): string

const [errordata] ?= action(argument1, argument2, ...)
// const [errordata] = action[Symbol.result](argument, argument2, ...)

?= 运算符与对象一起使用时,不会将任何参数传递给 Symbol.result 方法。

declare const obj: { [Symbol.result]: () => any }

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

递归处理机制

在使用 [error, null] 数组时,一旦遇到第一个异常就会生成。然而,如果 [null, data] 数组中的数据也实现了 Symbol.result 方法,那么该方法将会被递归调用。

const obj = {
  [Symbol.result]() {
    return [
      null,
      {
        [Symbol.result]() {
          return [new Error("Error"), null]
        },
      },
    ]
  },
}

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

// error 是 Error('string')

这种行为有助于处理各种包含 Symbol.result 方法的 Promise 或对象:

  • async function(): Promise
  • function(): T
  • function(): T | Promise

处理 Promise

Promise 是除了 Function 之外,唯一可以与 ?= 操作符一起使用的实现。

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

你可能已经注意到 await ?= 可以一起使用,而且绝对没问题。由于递归处理特性,它们可以很好地组合在一起。







请到「今天看啥」查看全文