Peringatan
Proposal ini akan berubah menjadi try-expressions
karena ini adalah apporach yang lebih idiomatik untuk masalah ini. Baca lebih lanjut di #4 dan #5.
Bantuan dalam penulisan ulang diperlukan :)
Peringatan
Proposal ini secara aktif sedang dikembangkan, dan kontribusi dipersilakan.
Proposal ini memperkenalkan operator baru , ?=
(penugasan aman) , yang menyederhanakan penanganan kesalahan dengan mengubah hasil fungsi menjadi tuple. Jika fungsi melempar kesalahan, operator mengembalikan [error, null]
; Jika fungsi berhasil dijalankan, ia mengembalikan [null, result]
. Operator ini kompatibel dengan janji, fungsi async, dan nilai apa pun yang mengimplementasikan metode Symbol.result
.
Misalnya, saat melakukan operasi I/O atau berinteraksi dengan API berbasis janji, kesalahan dapat terjadi secara tak terduga saat runtime. Mengabaikan untuk menangani kesalahan ini dapat menyebabkan perilaku yang tidak diinginkan dan potensi kerentanan keamanan.
const [ error , response ] ? = await fetch ( "https://arthur.place" )
Symbol.result
?=
)using
pernyataandata
terlebih dahulu??=
Dengan fungsi dan objek tanpa Symbol.result
Seberapa sering Anda melihat kode seperti ini?
async function getData ( ) {
const response = await fetch ( "https://api.example.com/data" )
const json = await response . json ( )
return validationSchema . parse ( json )
}
Masalah dengan fungsi di atas adalah bahwa ia dapat gagal secara diam -diam, berpotensi menabrak program Anda tanpa peringatan eksplisit.
fetch
bisa ditolak.json
bisa menolak.parse
bisa melempar. Untuk mengatasi ini, kami mengusulkan adopsi operator baru , ?=
, yang memfasilitasi penanganan kesalahan yang lebih ringkas dan dapat dibaca.
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
}
Silakan merujuk pada apa proposal ini tidak bertujuan untuk memecahkan bagian untuk memahami batasan proposal ini.
Proposal ini bertujuan untuk memperkenalkan fitur -fitur berikut:
Symbol.result
Objek apa pun yang mengimplementasikan Symbol.result
Metode RESULT dapat digunakan dengan 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
harus mengembalikan tuple, di mana elemen pertama mewakili kesalahan dan elemen kedua mewakili hasilnya.
Mengapa tidak data
terlebih dahulu?
?=
) 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)
Hasilnya harus sesuai dengan format [error, null | undefined]
atau [null, data]
.
Ketika operator ?=
Digunakan dalam suatu fungsi, semua parameter yang diteruskan ke fungsi itu diteruskan ke Symbol.result
. Metode Hasil.
declare function action ( argument : string ) : string
const [ error , data ] ? = action ( argument1 , argument2 , ... )
// const [error, data] = action[Symbol.result](argument, argument2, ...)
Ketika operator ?=
Digunakan dengan suatu objek, tidak ada parameter yang diteruskan ke Symbol.result
. Metode Hasil.
declare const obj : { [ Symbol . result ] : ( ) => any }
const [ error , data ] ? = obj
// const [error, data] = obj[Symbol.result]()
Tuple [error, null]
dihasilkan pada kesalahan pertama yang ditemui. Namun, jika data
dalam tuple [null, data]
juga mengimplementasikan metode Symbol.result
. Hasil, itu akan dipanggil secara rekursif.
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')
Perilaku ini memfasilitasi penanganan berbagai situasi yang melibatkan janji atau objek dengan Symbol.result
. Metode hasil:
async function(): Promise<T>
function(): T
function(): T | Promise<T>
Kasus -kasus ini mungkin melibatkan 0 hingga 2 tingkat objek bersarang dengan Symbol.result
Metode hasil, dan operator dirancang untuk menangani semuanya dengan benar.
Promise
adalah satu -satunya implementasi lain, selain Function
, yang dapat digunakan dengan operator ?=
const promise = getPromise ( )
const [ error , data ] ? = await promise
// const [error, data] = await promise[Symbol.result]()
Anda mungkin memperhatikan bahwa await
dan ?=
Dapat digunakan bersama, dan itu baik -baik saja. Karena fitur penanganan rekursif, tidak ada masalah dengan menggabungkannya dengan cara ini.
const [ error , data ] ? = await getPromise ( )
// const [error, data] = await getPromise[Symbol.result]()
Eksekusi akan mengikuti pesanan ini:
getPromise[Symbol.result]()
mungkin melempar kesalahan saat dipanggil (jika itu fungsi sinkron mengembalikan janji).error
, dan eksekusi akan berhenti.data
. Karena data
adalah janji dan janji memiliki Symbol.result
. Metode hasil, itu akan ditangani secara rekursif.error
, dan eksekusi akan berhenti.data
.using
pernyataan Pernyataan using
atau await using
juga harus bekerja dengan operator ?=
. Ini akan berkinerja serupa dengan standar using x = y
pernyataan.
Perhatikan bahwa kesalahan yang dilemparkan saat membuang sumber daya tidak ditangkap oleh ?=
Operator, sama seperti mereka tidak ditangani oleh fitur lain saat ini.
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
Aliran manajemen using
hanya diterapkan ketika error
adalah null
atau undefined
, dan a
adalah kebenaran dan memiliki Symbol.dispose
. Metode yang tidak disposisi.
Blok try {}
jarang berguna, karena pelingkupannya tidak memiliki signifikansi konseptual. Ini sering berfungsi lebih sebagai anotasi kode daripada konstruksi aliran kontrol. Tidak seperti blok aliran kontrol, tidak ada status program yang hanya bermakna dalam blok try {}
.
Sebaliknya, blok catch {}
adalah aliran kontrol aktual, dan pelingkupannya bermakna dan relevan.
Menggunakan Blok try/catch
memiliki dua masalah sintaks utama :
// 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
terlebih dahulu? Dalam go, konvensi ini adalah untuk menempatkan variabel data terlebih dahulu, dan Anda mungkin bertanya -tanya mengapa kami tidak mengikuti pendekatan yang sama di JavaScript. Di Go, ini adalah cara standar untuk memanggil fungsi. Namun, dalam JavaScript, kami sudah memiliki opsi untuk menggunakan const data = fn()
dan memilih untuk mengabaikan kesalahan, yang justru masalah yang kami coba atasi.
Jika seseorang menggunakan ?=
Sebagai operator penugasan mereka, itu karena mereka ingin memastikan bahwa mereka menangani kesalahan dan menghindari melupakannya. Menempatkan data terlebih dahulu akan bertentangan dengan prinsip ini, karena memprioritaskan hasil daripada penanganan kesalahan.
// 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 ( )
Jika Anda ingin menekan kesalahan (yang berbeda dari mengabaikan kemungkinan fungsi melempar kesalahan), Anda dapat melakukan hal berikut:
// This suppresses the error (ignores it and doesn't re-throw it)
const [ , data ] ? = fn ( )
Pendekatan ini jauh lebih eksplisit dan dapat dibaca karena mengakui bahwa mungkin ada kesalahan, tetapi menunjukkan bahwa Anda tidak peduli.
Metode di atas juga dikenal sebagai "cobalah calaboca" (istilah Brasil) dan dapat ditulis ulang sebagai:
let data
try {
data = fn ( )
} catch { }
Diskusi lengkap tentang topik ini di #13 jika pembaca tertarik.
Proposal ini dapat dipoles menggunakan kode yang disediakan di polyfill.js
.
Namun, operator ?=
Sendiri tidak dapat dipoles secara langsung. Saat menargetkan lingkungan JavaScript yang lebih lama, post-prosesor harus digunakan untuk mengubah ?=
Operator menjadi panggilan [Symbol.result]
yang sesuai.
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 ] ( )
?=
Dengan fungsi dan objek tanpa Symbol.result
Jika fungsi TypeError
objek tidak mengimplementasikan metode Symbol.result
?=
?=
Symbol.result
Faktanya, semua yang ingin dicapai proposal ini sudah dapat dicapai dengan fitur bahasa saat ini, meskipun verbose dan rawan kesalahan .
try {
// try expression
} catch ( error ) {
// catch code
}
// or
promise // try expression
. catch ( ( error ) => {
// catch code
} )
setara dengan:
const [ error , data ] ? = expression
if ( error ) {
// catch code
} else {
// try code
}
Pola ini hadir secara arsitektur dalam banyak bahasa:
?
OperatorResult
try?
Operatortry
kata kunci Sementara proposal ini tidak dapat menawarkan tingkat keamanan atau ketegangan jenis yang sama dengan bahasa -bahasa ini - karena sifat dinamis Javascript dan fakta bahwa pernyataan throw
dapat melempar apa pun - bertujuan untuk membuat kesalahan penanganan lebih konsisten dan dapat dikelola.
Jenis penegakan ketat untuk kesalahan : Pernyataan throw
dalam JavaScript dapat melempar segala jenis nilai. Proposal ini tidak memaksakan keselamatan jenis pada penanganan kesalahan dan tidak akan memasukkan jenis ke dalam bahasa. Itu juga tidak akan diperluas ke naskah. Untuk informasi lebih lanjut, lihat Microsoft/TypeScript#13219.
Penanganan kesalahan otomatis : Meskipun proposal ini memfasilitasi penanganan kesalahan, ia tidak secara otomatis menangani kesalahan untuk Anda. Anda masih perlu menulis kode yang diperlukan untuk mengelola kesalahan; Proposal ini hanya bertujuan untuk membuat proses ini lebih mudah dan lebih konsisten.
Sementara proposal ini masih dalam tahap awal, kami menyadari beberapa batasan dan bidang yang membutuhkan pengembangan lebih lanjut:
Nomenklatur untuk Symbol.result
Metode Hasil : Kita perlu menetapkan istilah untuk objek dan fungsi yang menerapkan Symbol.result
Metode Hasil. Ketentuan yang mungkin termasuk yang dapat dihasilkan atau kesalahan , tetapi ini perlu didefinisikan.
Penggunaan this
: Perilaku this
dalam konteks Symbol.result
belum diuji atau didokumentasikan. Ini adalah area yang membutuhkan eksplorasi dan dokumentasi lebih lanjut.
Penanganan finally
Blok : Saat ini tidak ada perbaikan sintaks untuk menangani blok finally
. Namun, Anda masih dapat menggunakan blok finally
seperti biasanya:
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
}
Proposal ini masih dalam tahap awal, dan kami menyambut input Anda untuk membantu memperbaikinya. Silakan membuka masalah atau mengirimkan permintaan tarik dengan saran Anda.
Kontribusi apa pun diterima!
tuple-it
NPM, yang memperkenalkan konsep yang sama tetapi memodifikasi Promise
dan Function
prototipe-pendekatan yang kurang ideal.Proposal ini dilisensikan di bawah lisensi MIT.