Осторожность
Это предложение изменится, чтобы try-expressions
как более идиоматическую оценку этой проблемы. Узнайте больше на № 4 и № 5.
Помощь в его переписывании необходима :)
Предупреждение
Это предложение активно разрабатывается, и взносы приветствуются.
Это предложение вводит нового оператора,? ?=
(Безопасное назначение) , который упрощает обработку ошибок, превращая результат функции в кортеж. Если функция бросает ошибку, оператор возвращает [error, null]
; Если функция выполняется успешно, она возвращает [null, result]
. Этот оператор совместим с обещаниями, асинхронными функциями и любым значением, которое реализует метод Symbol.result
.
Например, при выполнении операций ввода-вывода или взаимодействия с 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]
корзин генерируется при первой встрече с ошибкой. Однако, если data
в [null, data]
COUPLE также реализует метод 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>
Эти случаи могут включать в себя от 0 до 2 уровней вложенных объектов с Symbol.result
. Ресюльтные методы, и оператор предназначен для правильного обращения со всеми.
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 ( )
Этот подход гораздо более четко и читабелен, потому что он признает, что может быть ошибка, но указывает на то, что вам это не волнует.
Приведенный выше метод также известен как «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
может бросить что угодно - оно направлено на то, чтобы обработать ошибки более последовательной и управляемой.
Строгое тип применение ошибок : оператор throw
в JavaScript может добавить любой тип значения. Это предложение не налагает безопасность типа при обработке ошибок и не будет вводить типы в язык. Это также не будет распространено на TypeScript. Для получения дополнительной информации см. Microsoft/TypeScript#13219.
Автоматическая обработка ошибок : хотя это предложение облегчает обработку ошибок, оно автоматически не обрабатывает ошибки для вас. Вам все равно нужно будет написать необходимый код для управления ошибками; Предложение просто направлено на то, чтобы сделать этот процесс проще и последовательным.
Хотя это предложение все еще находится на ранних стадиях, мы знаем о нескольких ограничениях и областях, которые требуют дальнейшего развития:
Номенклатура для Symbol.result
Методы : нам необходимо установить термин для объектов и функций, которые реализуют методы Symbol.result
. Возможные термины включают в себя результаты или ошибочные , но это необходимо определить.
Использование this
: поведение this
в контексте Symbol.result
еще не было проверено или задокументировано. Это область, которая требует дальнейшего изучения и документации.
Обработка 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
, который вводит аналогичную концепцию, но изменяет прототипы Promise
и Function
-подход, который является менее идеальным.Это предложение лицензировано по лицензии MIT.