警告
该建议将变为try-expressions
,因为它是对此问题的更愚蠢的诉讼。在#4和#5上了解更多信息。
需要帮助重写:)
警告
该提案正在积极发展,欢迎捐款。
该建议介绍了一个新的操作员?=
(安全分配) ,这通过将功能的结果转换为元组来简化错误处理。如果函数引发错误,则操作员将返回[error, null]
;如果该函数成功执行,则返回[null, result]
。该操作员与承诺,异步函数以及实现Symbol.result
的任何值兼容。
例如,在执行I/O操作或与基于承诺的API进行交互时,错误可能在运行时出乎意料。忽略处理这些错误可能会导致意外行为和潜在的安全漏洞。
const [ error , response ] ? = await fetch ( "https://arthur.place" )
Symbol.result
?=
)using
语句data
??=
与无Symbol.result
函数和对象。您多久看一次这样的代码?
async function getData ( ) {
const response = await fetch ( "https://api.example.com/data" )
const json = await response . json ( )
return validationSchema . parse ( json )
}
上述功能的问题是,它可能会静静地失败,并可能在没有任何明确警告的情况下崩溃您的程序。
fetch
可以拒绝。json
可以拒绝。parse
可以投掷。为了解决这个问题,我们建议采用新操作员?=
,这有助于更简洁,可读性的错误处理。
async function getData ( ) {
const [ requestError , response ] ? = await fetch (
"https://api.example.com/data"
)
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
方法都可以与?=
operator一起使用。
function example ( ) {
return {
[ Symbol . result ] ( ) {
return [ new Error ( "123" ) , null ]
} ,
}
}
const [ error , result ] ? = example ( ) // Function.prototype also implements Symbol.result
// const [error, result] = example[Symbol.result]()
// error is Error('123')
Symbol.result
方法必须返回元组,其中第一个元素代表错误,第二个元素代表结果。
为什么不首先data
?
?=
) ?=
操作员在操作员右侧的对象或函数上调用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 [ error , data ] ? = action ( argument1 , argument2 , ... )
// const [error, data] = 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]
元组中的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 is Error('string')
这些行为有助于处理各种情况,涉及具有Symbol.result
的诺言或对象。解说方法:
async function(): Promise<T>
function(): T
function(): T | Promise<T>
这些情况可能涉及带有Symbol.result
的嵌套对象的0到2个级别,并且操作员旨在正确处理所有它们。
Promise
是唯一的其他实现,除Function
外,可以与?=
员一起使用。
const promise = getPromise ( )
const [ error , data ] ? = await promise
// const [error, data] = await promise[Symbol.result]()
您可能已经注意到了await
和?=
可以一起使用,这很好。由于递归处理功能,以这种方式将它们结合在一起没有问题。
const [ error , data ] ? = await getPromise ( )
// const [error, data] = await getPromise[Symbol.result]()
执行将遵循此订单:
getPromise[Symbol.result]()
可能会在调用时会丢失错误(如果是同步函数返回承诺)。error
,并且执行将停止。data
。由于data
是一个承诺,并且承诺具有Symbol.result
。结果方法将递归处理。error
,并且执行将停止。data
。using
语句using
或await using
也应与?=
操作员一起使用。它将using x = y
语句与标准类似。
请注意,处置资源时丢弃的错误不会被?=
员捕获,就像其他当前功能无法处理的那样。
try {
using a = b
} catch ( error ) {
// handle
}
// now becomes
using [ error , a ] ? = b
// or with async
try {
await using a = b
} catch ( error ) {
// handle
}
// now becomes
await using [ error , a ] ? = b
仅当error
为null
或undefined
时才应用using
管理流,并且a
是真实的并且具有Symbol.dispose
。
try {}
块很少有用,因为其范围缺乏概念意义。它通常可以用作代码注释,而不是控制流构建体。与控制流量块不同,没有程序状态仅在try {}
块中才有意义。
相反, catch {}
块是实际的控制流,其范围是有意义的和相关的。
使用try/catch
块有两个主要的语法问题:
// Nests 1 level for each error handling block
async function readData ( filename ) {
try {
const fileContent = await fs . readFile ( filename , "utf8" )
try {
const json = JSON . parse ( fileContent )
return json . data
} catch ( error ) {
handleJsonError ( error )
return
}
} catch ( error ) {
handleFileError ( error )
return
}
}
// Declares reassignable variables outside the block, which is undesirable
async function readData ( filename ) {
let fileContent
let json
try {
fileContent = await fs . readFile ( filename , "utf8" )
} catch ( error ) {
handleFileError ( error )
return
}
try {
json = JSON . parse ( fileContent )
} catch ( error ) {
handleJsonError ( error )
return
}
return json . data
}
data
?在GO中,该约定是将数据变量放在首位,您可能会想知道为什么我们在JavaScript中不遵循相同的方法。在Go中,这是调用功能的标准方法。但是,在JavaScript中,我们已经可以选择使用const data = fn()
并选择忽略错误,这正是我们要解决的问题。
如果有人使用?=
作为他们的分配运营商,那是因为他们想确保他们处理错误并避免忘记他们。首先放置数据将与此原理相矛盾,因为它优先考虑结果而不是错误处理。
// ignores errors!
const data = fn ( )
// Look how simple it is to forget to handle the error
const [ data ] ? = fn ( )
// This is the way to go
const [ error , data ] ? = fn ( )
如果要抑制错误(这与忽略函数丢弃错误的可能性不同),则可以简单地执行以下操作:
// This suppresses the error (ignores it and doesn't re-throw it)
const [ , data ] ? = fn ( )
这种方法更加明确和可读性,因为它承认可能存在错误,但表明您不在乎它。
上述方法也称为“ try-catch calaboca”(巴西术语),可以重写为:
let data
try {
data = fn ( )
} catch { }
如果读者有兴趣,请在#13上完成有关此主题的讨论。
可以使用polyfill.js
提供的代码进行多填充此建议。
但是, ?=
操作员本身不能直接填充。针对较旧的JavaScript环境时,应使用后处理器将?=
运算符转换为相应的[Symbol.result]
调用。
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 ] ( )
?=
与无Symbol.result
函数和对象。如果函数或对象不实现Symbol.result
方法,则?=
操作员应该扔一个TypeError
。
?=
运算符和Symbol.result
结果建议不要将新逻辑引入语言。实际上,该提案旨在实现的一切都可以通过目前的语言功能来实现,尽管目前是冗长和错误的语言功能。
try {
// try expression
} catch ( error ) {
// catch code
}
// or
promise // try expression
. catch ( ( error ) => {
// catch code
} )
等同于:
const [ error , data ] ? = expression
if ( error ) {
// catch code
} else {
// try code
}
这种模式在架构上以多种语言存在:
?
操作员Result
类型try?
操作员try
关键字尽管该提案不能提供与这些语言相同的类型安全性或严格性的水平,而是JavaScript的动态性质以及throw
声明可以抛出任何东西的事实,它旨在使错误处理更加一致和可管理。
严格的错误类型执行:JavaScript中的throw
语句可以抛出任何类型的值。该提案不会在错误处理中强加类型的安全性,也不会将类型引入该语言。它也不会扩展到打字稿。有关更多信息,请参见Microsoft/Typescript#13219。
自动错误处理:虽然此提案有助于处理错误,但并不能自动处理错误。您仍然需要编写必要的代码来管理错误;该提案只是旨在使这个过程更轻松,更一致。
尽管该提案仍处于早期阶段,但我们知道需要进一步发展的几个局限性和领域:
Symbol.result
方法的命名法:我们需要为实现Symbol.result
对象和函数建立一个术语。可能的术语包括可产生的或可误的,但这需要定义。
this
:在Symbol.result
的上下文中, this
行为尚未被测试或记录。这是一个需要进一步探索和文档的领域。
处理finally
块:当前没有用于处理finally
块的语法改进。但是,您仍然可以像往常一样使用finally
块:
try {
// try code
} catch {
// catch errors
} finally {
// finally code
}
// Needs to be done as follows
const [ error , data ] ? = action ( )
try {
if ( error ) {
// catch errors
} else {
// try code
}
} finally {
// finally code
}
该提案处于早期阶段,我们欢迎您的意见来帮助完善它。请随时开放问题或提交您的建议。
欢迎任何贡献!
tuple-it
NPM软件包引入了类似的概念,但会修改了Promise
和Function
原型,这种方法不太理想。该提案已根据麻省理工学院许可获得许可。