Ini adalah penjelasan untuk proposal API yang dapat diamati untuk penanganan peristiwa yang lebih ergonomis dan komposisi.
EventTarget Proposal ini menambahkan metode .when() ke EventTarget yang menjadi addEventListener() ; Secara khusus ia mengembalikan yang Observable baru yang menambahkan pendengar acara baru ke target ketika metode subscribe() dipanggil. Panggilan yang dapat diamati sebagai penangan pelanggan next() dengan setiap acara.
Observable mengubah penanganan acara, penyaringan, dan penghentian, menjadi aliran deklaratif yang eksplisit yang lebih mudah dipahami dan ditulis daripada versi imperatif saat ini, yang sering membutuhkan panggilan bersarang untuk addEventListener() dan rantai panggilan balik yang sulit diikuti.
// Filtering and mapping:
element
. when ( 'click' )
. filter ( ( e ) => e . target . matches ( '.foo' ) )
. map ( ( e ) => ( { x : e . clientX , y : e . clientY } ) )
. subscribe ( { next : handleClickAtPoint } ) ; // Automatic, declarative unsubscription via the takeUntil method:
element . when ( 'mousemove' )
. takeUntil ( document . when ( 'mouseup' ) )
. subscribe ( { next : e => … } ) ;
// Since reduce and some other terminators return promises, they also play
// well with async functions:
await element . when ( 'mousemove' )
. takeUntil ( element . when ( 'mouseup' ) )
. reduce ( ( soFar , e ) => … ) ; // Imperative
const controller = new AbortController ( ) ;
element . addEventListener ( 'mousemove' , e => {
console . log ( e ) ;
element . addEventListener ( 'mouseup' , e => {
controller . abort ( ) ;
} ) ;
} , { signal : controller . signal } ) ; Melacak semua klik tautan dalam wadah (contoh):
container
. when ( 'click' )
. filter ( ( e ) => e . target . closest ( 'a' ) )
. subscribe ( {
next : ( e ) => {
// …
} ,
} ) ; Temukan koordinat y maksimum saat mouse ditahan (contoh):
const maxY = await element
. when ( 'mousemove' )
. takeUntil ( element . when ( 'mouseup' ) )
. map ( ( e ) => e . clientY )
. reduce ( ( soFar , y ) => Math . max ( soFar , y ) , 0 ) ; Multiplexing WebSocket , sehingga pesan berlangganan dikirim pada koneksi, dan pesan berhenti berlangganan dikirim ke server ketika pengguna tidak berlangganan.
const socket = new WebSocket ( 'wss://example.com' ) ;
function multiplex ( { startMsg , stopMsg , match } ) {
if ( socket . readyState !== WebSocket . OPEN ) {
return socket
. when ( 'open' )
. flatMap ( ( ) => multiplex ( { startMsg , stopMsg , match } ) ) ;
} else {
socket . send ( JSON . stringify ( startMsg ) ) ;
return socket
. when ( 'message' )
. filter ( match )
. takeUntil ( socket . when ( 'close' ) )
. takeUntil ( socket . when ( 'error' ) )
. map ( ( e ) => JSON . parse ( e . data ) )
. finally ( ( ) => {
socket . send ( JSON . stringify ( stopMsg ) ) ;
} ) ;
}
}
function streamStock ( ticker ) {
return multiplex ( {
startMsg : { ticker , type : 'sub' } ,
stopMsg : { ticker , type : 'unsub' } ,
match : ( data ) => data . ticker === ticker ,
} ) ;
}
const googTrades = streamStock ( 'GOOG' ) ;
const nflxTrades = streamStock ( 'NFLX' ) ;
const googController = new AbortController ( ) ;
googTrades . subscribe ( { next : updateView } , { signal : googController . signal } ) ;
nflxTrades . subscribe ( { next : updateView , ... } ) ;
// And the stream can disconnect later, which
// automatically sends the unsubscription message
// to the server.
googController . abort ( ) ; // Imperative
function multiplex ( { startMsg , stopMsg , match } ) {
const start = ( callback ) => {
const teardowns = [ ] ;
if ( socket . readyState !== WebSocket . OPEN ) {
const openHandler = ( ) => start ( { startMsg , stopMsg , match } ) ( callback ) ;
socket . addEventListener ( 'open' , openHandler ) ;
teardowns . push ( ( ) => {
socket . removeEventListener ( 'open' , openHandler ) ;
} ) ;
} else {
socket . send ( JSON . stringify ( startMsg ) ) ;
const messageHandler = ( e ) => {
const data = JSON . parse ( e . data ) ;
if ( match ( data ) ) {
callback ( data ) ;
}
} ;
socket . addEventListener ( 'message' , messageHandler ) ;
teardowns . push ( ( ) => {
socket . send ( JSON . stringify ( stopMsg ) ) ;
socket . removeEventListener ( 'message' , messageHandler ) ;
} ) ;
}
const finalize = ( ) => {
teardowns . forEach ( ( t ) => t ( ) ) ;
} ;
socket . addEventListener ( 'close' , finalize ) ;
teardowns . push ( ( ) => socket . removeEventListener ( 'close' , finalize ) ) ;
socket . addEventListener ( 'error' , finalize ) ;
teardowns . push ( ( ) => socket . removeEventListener ( 'error' , finalize ) ) ;
return finalize ;
} ;
return start ;
}
function streamStock ( ticker ) {
return multiplex ( {
startMsg : { ticker , type : 'sub' } ,
stopMsg : { ticker , type : 'unsub' } ,
match : ( data ) => data . ticker === ticker ,
} ) ;
}
const googTrades = streamStock ( 'GOOG' ) ;
const nflxTrades = streamStock ( 'NFLX' ) ;
const unsubGoogTrades = googTrades ( updateView ) ;
const unsubNflxTrades = nflxTrades ( updateView ) ;
// And the stream can disconnect later, which
// automatically sends the unsubscription message
// to the server.
unsubGoogTrades ( ) ; Di sini kami memanfaatkan yang dapat diamati untuk mencocokkan kode rahasia, yang merupakan pola kunci yang mungkin dipukul pengguna saat menggunakan aplikasi:
const pattern = [
'ArrowUp' ,
'ArrowUp' ,
'ArrowDown' ,
'ArrowDown' ,
'ArrowLeft' ,
'ArrowRight' ,
'ArrowLeft' ,
'ArrowRight' ,
'b' ,
'a' ,
'b' ,
'a' ,
'Enter' ,
] ;
const keys = document . when ( 'keydown' ) . map ( e => e . key ) ;
keys
. flatMap ( firstKey => {
if ( firstKey === pattern [ 0 ] ) {
return keys
. take ( pattern . length - 1 )
. every ( ( k , i ) => k === pattern [ i + 1 ] ) ;
}
} )
. filter ( matched => matched )
. subscribe ( ( ) => console . log ( 'Secret code matched!' ) ) ; const pattern = [ ... ] ;
// Imperative
document . addEventListener ( 'keydown' , e => {
const key = e . key ;
if ( key === pattern [ 0 ] ) {
let i = 1 ;
const handler = ( e ) => {
const nextKey = e . key ;
if ( nextKey !== pattern [ i ++ ] ) {
document . removeEventListener ( 'keydown' , handler )
} else if ( pattern . length === i ) {
console . log ( 'Secret code matched!' ) ;
document . removeEventListener ( 'keydown' , handler )
}
} ;
document . addEventListener ( 'keydown' , handler ) ;
}
} , { once : true } ) ;Observable Observable adalah objek kelas satu yang mewakili peristiwa yang dapat dikomposisikan dan berulang. Mereka seperti janji tetapi untuk beberapa acara, dan khususnya dengan integrasi EventTarget , mereka harus melakukan acara apa janji untuk panggilan balik. Mereka bisa:
subscribe()Observable.map() , untuk dikomposisikan & diubah tanpa jaringan callback bersarang Lebih baik lagi, transisi dari penangan acara ➡️ Observable lebih sederhana daripada janji callback ➡️, karena yang dapat diamati diintegrasikan dengan baik di atas EventTarget , cara de facto berlangganan acara dari platform dan skrip khusus. Akibatnya, pengembang dapat menggunakan yang dapat diamati tanpa memigrasi banyak kode pada platform, karena ini adalah drop-in yang mudah di mana pun Anda menangani acara hari ini.
Bentuk API yang diusulkan dapat ditemukan di https://wicg.github.io/observable/#core-infrastruktur.
Pencipta operan yang dapat diamati dalam panggilan balik yang dipanggil secara serempak setiap kali subscribe() dipanggil. Metode subscribe() dapat disebut beberapa kali , dan panggilan balik yang dipanggilnya mengatur "berlangganan" baru dengan mendaftarkan penelepon subscribe() sebagai pengamat. Dengan hal ini, yang dapat diamati dapat menandakan sejumlah peristiwa kepada pengamat melalui panggilan balik next() , secara opsional diikuti oleh satu panggilan untuk complete() atau error() , menandakan bahwa aliran data selesai.
const observable = new Observable ( ( subscriber ) => {
let i = 0 ;
setInterval ( ( ) => {
if ( i >= 10 ) subscriber . complete ( ) ;
else subscriber . next ( i ++ ) ;
} , 2000 ) ;
} ) ;
observable . subscribe ( {
// Print each value the Observable produces.
next : console . log ,
} ) ; Sementara yang dapat diamati secara khusus dapat berguna sendiri, kasus penggunaan utama yang mereka buka adalah dengan penanganan acara. Metode Observable yang dikembalikan oleh Metode EventTarget#when() baru dibuat secara asli dengan panggilan balik internal yang menggunakan mekanisme mendasar yang sama dengan addEventListener() . Oleh karena itu memanggil subscribe() pada dasarnya mendaftarkan pendengar acara baru yang acaranya diekspos melalui fungsi pengamat pawang dan dapat dikomposisikan dengan berbagai kombinator yang tersedia untuk semua yang dapat diamati.
Observable dapat dibuat oleh konstruktor aslinya, seperti yang ditunjukkan di atas, atau dengan metode statis Observable.from() . Metode ini membangun asli yang dapat diamati dari objek yang merupakan salah satu dari yang berikut, dalam urutan ini :
Observable (dalam hal ini hanya mengembalikan objek yang diberikan)AsyncIterable (apa pun dengan Symbol.asyncIterator )Iterable (apa pun dengan Symbol.iterator )Promise (atau yang dapat kemudian) Selain itu, metode apa pun pada platform yang ingin menerima yang dapat diamati sebagai argumen IDL web, atau mengembalikan satu dari panggilan balik yang jenis pengembaliannya dapat Observable dengan salah satu objek di atas juga, yang secara otomatis dikonversi menjadi yang dapat diamati. Kami dapat mencapai ini dalam salah satu dari dua cara yang akan kami selesaikan dalam spesifikasi yang dapat diamati:
Observable jenis web IDL khusus yang melakukan objek ecmascript ini ➡️ Konversi Web IDL secara otomatis, seperti yang dilakukan Web IDL untuk jenis lain.any , dan memiliki prosa spesifikasi yang sesuai segera memohon algoritma konversi yang akan disediakan oleh spesifikasi yang dapat diamati. Ini mirip dengan apa yang dilakukan standar aliran dengan async iterable hari ini.Percakapan di #60 bersandar pada opsi (1).
Yang terpenting, dapat diamati adalah "malas" karena mereka tidak mulai memancarkan data sampai mereka berlangganan, mereka juga tidak mengantri data apa pun sebelum berlangganan. Mereka juga dapat mulai memancarkan data secara serempak selama berlangganan, tidak seperti janji yang selalu mengantri microtasks saat memohon .then() penangan. Pertimbangkan contoh ini:
el . when ( 'click' ) . subscribe ( { next : ( ) => console . log ( 'One' ) } ) ;
el . when ( 'click' ) . find ( ( ) => { … } ) . then ( ( ) => console . log ( 'Three' ) ) ;
el . click ( ) ;
console . log ( 'Two' ) ;
// Logs "One" "Two" "Three" Dengan menggunakan AbortController , Anda dapat berhenti berlangganan dari yang dapat diamati meskipun secara sinkron memancarkan data selama berlangganan:
// An observable that synchronously emits unlimited data during subscription.
let observable = new Observable ( ( subscriber ) => {
let i = 0 ;
while ( true ) {
subscriber . next ( i ++ ) ;
}
} ) ;
let controller = new AbortController ( ) ;
observable . subscribe ( {
next : ( data ) => {
if ( data > 100 ) controller . abort ( ) ;
} } , { signal : controller . signal } ,
} ) ; Sangat penting bagi pelanggan yang dapat diamati untuk dapat mendaftarkan panggilan balik robekan sewenang -wenang untuk membersihkan sumber daya yang relevan dengan langganan. Teardown dapat didaftarkan dari dalam panggilan balik berlangganan yang diteruskan ke konstruktor Observable . Saat dijalankan (setelah berlangganan), panggilan balik berlangganan dapat mendaftarkan fungsi robekan melalui subscriber.addTeardown() .
Jika pelanggan telah dibatalkan ( true , subscriber.signal.aborted addTeardown() Kalau tidak, itu dipanggil secara sinkron:
complete() , setelah pawang lengkap pelanggan (jika ada) dipanggilerror() , setelah penangan kesalahan pelanggan (jika ada) dipanggil Kami mengusulkan operator berikut selain antarmuka Observable :
catch()Promise#catch() , dibutuhkan panggilan balik yang dipecat setelah sumber kesalahan yang dapat diamati. Ini kemudian akan memetakan ke yang baru dapat diamati, dikembalikan dengan panggilan balik, kecuali kesalahannya diputar ulang.takeUntil(Observable)finally()Promise.finally() , dibutuhkan panggilan balik yang dipecat setelah yang dapat diamati selesai dengan cara apa pun ( complete() / error() ).Observable yang mencerminkan sumber yang dapat diamati dengan tepat. Panggilan balik yang diteruskan ke finally dipecat ketika berlangganan yang dapat diamati dihentikan karena alasan apa pun . Segera setelah sumber selesai atau kesalahan, atau ketika konsumen berhenti berlangganan dengan membatalkan langganan. Versi di atas sering hadir dalam implementasi Userland dari Observable karena mereka berguna untuk alasan khusus yang dapat diamati, tetapi selain ini kami menawarkan satu set operator umum yang mengikuti preseden platform yang ada dan dapat sangat meningkatkan utilitas dan adopsi. Ini ada pada iterables lainnya, dan berasal dari proposal TC39 Iterator Helters yang menambahkan metode berikut ke Iterator.prototype :
map()filter()take()drop()flatMap()reduce()toArray()forEach()some()every()find() Dan metode berikut secara statis pada konstruktor Iterator :
from() Kami mengharapkan perpustakaan Userland untuk menyediakan lebih banyak operator niche yang berintegrasi dengan API Central Observable untuk proposal ini, berpotensi pengiriman secara asli jika mereka mendapatkan momentum yang cukup untuk lulus ke platform. Tetapi untuk proposal awal ini, kami ingin membatasi set operator untuk mereka yang mengikuti preseden yang disebutkan di atas, mirip dengan bagaimana API platform web yang dinyatakan seperti set dan seperti peta memiliki properti asli yang terinspirasi oleh peta TC39 dan set objek. Oleh karena itu kami akan mempertimbangkan sebagian besar diskusi tentang memperluas set ini sebagai di luar lingkup untuk proposal awal , cocok untuk diskusi dalam lampiran. Ekor panjang operator dapat mengikuti jika ada dukungan untuk API asli yang dapat diamati yang disajikan dalam penjelasan ini.
Perhatikan bahwa operator every() , find() , some() , dan reduce() janji pengembalian yang penjadwalannya berbeda dari yang dapat diamati, yang kadang -kadang berarti penangan peristiwa yang memanggil e.preventDefault() akan berjalan terlalu terlambat. Lihat bagian Kekhawatiran yang lebih detail.
Untuk menggambarkan bagaimana Observable cocok dengan lanskap saat ini dari primitif reaktif lainnya, lihat tabel di bawah ini yang merupakan upaya menggabungkan dua tabel lain yang mengklasifikasikan primitif reaktif dengan interaksi mereka dengan produsen & konsumen:
| Tunggal | Jamak | |||
|---|---|---|---|---|
| Spasial | Sementara | Spasial | Sementara | |
| Dorongan | Nilai | Janji | Tampak | |
| Menarik | Fungsi | Async iterator | Iterable | Async iterator |
Observable pertama kali diusulkan ke platform di TC39 pada bulan Mei 2015. Proposal gagal mendapatkan daya tarik, sebagian karena beberapa oposisi bahwa API cocok untuk menjadi primitif tingkat bahasa. Dalam upaya untuk memperbarui proposal pada tingkat abstraksi yang lebih tinggi, masalah Whatwg DOM diajukan pada bulan Desember 2017. Meskipun ada banyak permintaan pengembang, banyak diskusi, dan tidak ada penentang yang kuat, proposal DOM yang dapat diamati sebagian besar masih ada selama beberapa tahun (dengan beberapa fluks dalam desain API) karena kurangnya prioritas pelaksana.
Kemudian pada tahun 2019, upaya untuk menghidupkan kembali proposal dibuat kembali di repositori TC39 asli, yang melibatkan beberapa penyederhanaan API dan menambahkan dukungan untuk masalah "firehose" yang sinkron.
Repositori ini merupakan upaya untuk menghembuskan kembali kehidupan ke dalam proposal yang dapat diamati dengan harapan mengirimkan versi ke platform web.
Dalam diskusi sebelumnya, Ben Lesh telah mendaftarkan beberapa implementasi Userland khusus dari primitif yang dapat diamati, di mana RXJS adalah yang paling populer dengan "47.000.000 unduhan per minggu ."
start dan unsubscribe untuk pengamatan dan memperoleh Subscription sebelum pengembalian.Actor mereka, untuk memungkinkan langganan berubah dalam keadaan, seperti yang ditunjukkan dalam kait useActor . Menggunakan yang identik yang dapat diamati juga merupakan bagian terdokumentasi dari perubahan mesin negara bagian yang berubah saat menggunakan XState dengan SolidJS.{ subscribe(callback: (value: T) => void): () => void } pola dalam router dan kode DeferredData mereka. Ini ditunjukkan oleh pengelola yang diilhami oleh yang dapat diamati.{ subscribe(callback: (value: T) => void): () => void } antarmuka untuk sinyal mereka.{ subscribe(callback: (value: T) => void): () => void } di beberapa tempat{ subscribe(callback: (value: T) => void): () => void } .{ observe_(callback: (value: T)): () => void } .| async Fungsionalitas | async "Async Pipe" dalam templat.Mengingat seni sebelumnya yang luas di bidang ini, ada "kontrak yang dapat diamati" publik.
Selain itu banyak API JavaScript yang mencoba untuk mematuhi kontrak yang ditentukan oleh proposal TC39 dari 2015. Untuk tujuan itu, ada perpustakaan, simbol-yang dapat diabaikan, bahwa Symbol.observable ponyfills (polyfill). Dipertahankan untuk membantu dengan interoperabilitas antara tipe yang dapat diamati yang mematuhi persis antarmuka yang didefinisikan di sini. symbol-observable memiliki 479 paket dependen pada NPM, dan diunduh lebih dari 13.000.000 kali per minggu. Ini berarti bahwa ada minimal 479 paket pada NPM yang menggunakan kontrak yang dapat diamati dalam beberapa cara.
Ini mirip dengan bagaimana janji/spesifikasi A+ yang dikembangkan sebelum Promise S diadopsi menjadi ES2015 sebagai primitif bahasa kelas satu.
Salah satu kekhawatiran utama yang diungkapkan dalam Thread Dom Whatwg asli berkaitan dengan API yang dapat diamati dengan janji, seperti yang diusulkan first() . Potensi kaki potensial di sini dengan penjadwalan microtask dan integrasi acara. Secara khusus, kode yang tampak tidak bersalah berikut tidak akan selalu berfungsi:
element
. when ( 'click' )
. first ( )
. then ( ( e ) => {
e . preventDefault ( ) ;
// Do something custom...
} ) ; Jika Observable#first() mengembalikan janji yang diselesaikan ketika acara pertama ditembakkan pada EventTarget , maka janji yang disediakan pengguna .then() Handler akan berjalan:
element.click() )e.preventDefault() akan terjadi terlambat dan secara efektif diabaikanDi WebIDL setelah panggilan balik dipanggil, algoritma HTML membersihkan setelah menjalankan skrip dipanggil, dan panggilan algoritma ini melakukan pos pemeriksaan microtask jika dan hanya jika javascript tumpukan kosong.
Secara konkret, itu berarti untuk element.click() dalam contoh di atas, langkah -langkah berikut terjadi:
element.click() , konteks eksekusi JavaScript pertama kali didorong ke tumpukanclick internal (yang dibuat secara asli oleh implementasi Observable#from() ), konteks eksekusi JavaScript lainnya didorong ke tumpukan, saat WebIDL bersiap untuk menjalankan callback internal internalObservable#first() ; Sekarang antrian microtask berisi pawang yang disediakan pengguna then() yang akan membatalkan acara setelah berjalanclick internal dieksekusi, sisa jalur acara berlanjut karena acara tidak dibatalkan selama atau segera setelah panggilan balik. Acara melakukan apa pun yang biasanya dilakukannya (kirimkan formulir, alert() pengguna, dll.)element.click() selesai, dan konteks eksekusi akhir muncul dari tumpukan dan antrian microtask memerah. Pawang .then() Dua hal mengurangi kekhawatiran ini. Pertama, ada solusi yang sangat sederhana untuk selalu menghindari kasus di mana e.preventDefault() mungkin berjalan terlalu terlambat:
element
. when ( 'click' )
. map ( ( e ) => ( e . preventDefault ( ) , e ) )
. first ( ) ; ... atau jika dapat diamati memiliki metode .do() (lihat Whatwg/Dom#544 (komentar)):
element
. when ( 'click' )
. do ( ( e ) => e . preventDefault ( ) )
. first ( ) ; ... atau dengan memodifikasi semantik first() untuk menerima panggilan balik yang menghasilkan nilai yang diselesaikan oleh janji yang dikembalikan ke:
el . when ( 'submit' )
. first ( ( e ) => e . preventDefault ( ) )
. then ( doMoreStuff ) ;Kedua, "kekhasan" ini sudah ada di ekosistem yang dapat diamati saat ini, dan tidak ada kekhawatiran serius atau laporan dari komunitas yang secara konsisten mengalami hal ini. Ini memberi kepercayaan bahwa memanggang perilaku ini ke platform web tidak akan berbahaya.
Ada banyak diskusi tentang tempat standar mana yang pada akhirnya harus menjadi tuan rumah proposal yang dapat diamati. Tempat ini tidak penting, karena secara efektif memutuskan apakah Observable menjadi primitif tingkat bahasa seperti Promise , bahwa kapal di semua mesin browser JavaScript, atau platform web primitif dengan kemungkinan pertimbangan (tetapi secara teknis opsional ) di lingkungan lain seperti Node.js (lihat AbortController misalnya).
Observable dengan sengaja mengintegrasikan tanpa gesekan dengan antarmuka pemancar acara utama ( EventTarget ) dan pembatalan primitif ( AbortController ) yang hidup di platform web. Seperti yang diusulkan di sini, Observable bergabung dengan komponen yang sangat terhubung dengan kuat ini dari standar DOM: Observable tergantung pada abortController/abortSignal, yang bergantung pada target events, dan eventtarget tergantung pada baik yang dapat diamati dan abortController/abortSignal. Karena kami merasa bahwa yang dapat diamati paling cocok di mana primitif pendukungnya hidup, tempat standar WhatWG mungkin adalah tempat terbaik untuk memajukan proposal ini. Selain itu, embedder ecmascript non-WEB seperti Node.js dan Deno masih akan dapat mengadopsi yang dapat diamati, dan bahkan cenderung, mengingat komitmen mereka pada platform web yang dibatalkan dan acara.
Ini tidak menghalangi standardisasi primitif yang memancar dan pembatalan di masa depan di TC39 di masa depan, sesuatu yang dapat diamati secara teoritis dapat dilapisi di atas nanti. Tetapi untuk saat ini, kami termotivasi untuk membuat kemajuan dalam apa.
Dalam upaya untuk menghindari mereparasi diskusi ini, kami akan mendesak pembaca untuk melihat komentar diskusi berikut:
Bagian ini memamerkan kumpulan Standar Web dan Posisi Standar masalah yang digunakan untuk melacak kehidupan proposal yang dapat diamati di luar repositori ini.
Observable dirancang untuk membuat penanganan peristiwa lebih ergonomis dan dapat dikomposisikan. Dengan demikian, dampaknya terhadap pengguna akhir tidak langsung, sebagian besar datang dalam bentuk pengguna yang harus mengunduh lebih sedikit javascript untuk mengimplementasikan pola yang saat ini menggunakan pustaka pihak ketiga. Seperti yang dinyatakan di atas dalam penjelasan, ada ekosistem Userland Observable yang berkembang yang menghasilkan banyak byte berlebihan yang diunduh setiap hari.
Dalam upaya untuk mengkodifikasi preseden Userland yang kuat dari API yang dapat diamati, proposal ini akan menghemat lusinan implementasi khusus agar tidak diunduh setiap hari.
Selain itu, sebagai API seperti EventTarget , AbortController , dan yang terkait dengan Promise , memungkinkan pengembang untuk membangun aliran penanganan peristiwa yang kurang rumit dengan membangunnya secara deklaratif, yang memungkinkan mereka untuk membangun lebih banyak pengalaman pengguna yang sehat di web.