주의
이 제안은이 문제에 대한보다 관용적 인 apporach로서 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
구현하는 객체는 ?=
연산자와 함께 사용할 수 있습니다.
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
using
관리 흐름은 error
가 null
또는 undefined
경우에만 적용되며 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에서 동일한 접근법을 따르지 않는지 궁금 할 것입니다. 이동 중에는 기능을 호출하는 표준 방법입니다. 그러나 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
문은 모든 유형의 값을 던질 수 있습니다. 이 제안은 오류 처리에 유형 안전을 부과하지 않으며 언어에 유형을 도입하지 않습니다. 또한 TypeScript로 확장되지 않습니다. 자세한 내용은 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
}
이 제안은 초기 단계에 있으며,이를 개선하는 데 도움이되는 귀하의 의견을 환영합니다. 귀하의 제안과 함께 문제를 열거 나 풀 요청을 제출하십시오.
모든 공헌을 환영합니다!
Promise
및 Function
프로토 타입을 수정하는 tuple-it
NPM 패키지는 덜 이상적인 접근법입니다.이 제안은 MIT 라이센스에 따라 라이센스가 부여됩니다.