警告
該建議將變為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
原型,這種方法不太理想。該提案已根據麻省理工學院許可獲得許可。