Usulan penambahan Signals ke JavaScript
(github.com/proposal-signals)- Proposal JavaScript Signals dari TC39 adalah arah awal untuk menstandarkan primitif reaktif guna melacak status UI dan status terhitung secara efisien, dan saat ini masih berupa draf level Stage 1
- Proposal ini berfokus pada semantik inti graf Signal dan mekanisme pelacakan otomatis yang dapat dibagikan antar-framework, alih-alih API permukaan yang langsung digunakan pengembang aplikasi
Signal.State,Signal.Computed, danSignal.subtle.Watcheradalah API inti, dengan komputasi yang menargetkan lazy evaluation, caching, pelacakan dependensi otomatis, dan eksekusi bebas glitch- Tujuan Signals bawaan adalah meningkatkan interoperabilitas antar-framework seperti Angular, Ember, MobX, Preact, Qwik, Solid, Svelte, Vue, dan lainnya, sekaligus membuka kemungkinan dukungan debugging dan analisis performa di DevTools
- Kelompok proposal berencana melewati berbagai polyfill kelas produksi, integrasi framework, validasi pada aplikasi berskala besar, dan benchmark performa sebelum Stage 2, dan standardisasi kemungkinan memerlukan setidaknya 2~3 tahun atau lebih
Posisi proposal JavaScript Signals
- JavaScript Signals diperkenalkan sebagai proposal Stage 1 dalam proses TC39
- Dokumen saat ini adalah upaya untuk menyelaraskan arah bersama di ekosistem JavaScript, mirip dengan posisi Promises/A+ sebelum standardisasi
Promisedi ES2015 - Tersedia polyfill yang bisa langsung dicoba
- Champion proposal dan penulis asli menyusun draf saat ini berdasarkan masukan desain dari berbagai framework dan library
Masalah yang dibidik oleh standardisasi
- UI yang kompleks perlu menyimpan nilai, menghitung, menginvaliasi, menyinkronkan, dan mendorongnya ke lapisan view, dan Signals berupaya menyediakan infrastruktur manajemen status untuk pekerjaan berulang semacam ini
- Dalam contoh Vanilla JS,
counter,isEven,parity, danrendersaling terikat langsung sehingga menimbulkan masalah berikut- Status dan sistem rendering menjadi sangat tightly coupled
- Bahkan ketika
paritytidak berubah, seperti saatcounterberubah dari 2 ke 4, komputasi dan rendering yang tidak perlu tetap terjadi - Saat bagian UI lain ingin berlangganan hanya ke sebagian dari
counter,isEven, atauparity, pengelolaan subscribe dan unsubscribe manual menjadi rumit - Jika pub/sub ditambahkan ke banyak tahap, boilerplate dan bookkeeping subscription meningkat serta risiko memory leak pun muncul
- Contoh berbasis Signals menangani nilai, komputasi, dan efek samping dengan satu cara menggunakan
Signal.StatedanSignal.Computed- Tidak perlu subscription manual
- Signal terhitung secara otomatis menemukan Signal yang menjadi dependensinya
- Komputasi hanya dijalankan saat nilainya diminta secara eksplisit
- Signal terhitung menyimpan cache nilai terakhir
API inti proposal
Signal<T>didefinisikan sebagai nilai yang dapat dibaca denganget(): TSignal.State<T>adalah Signal yang dapat ditulis- Konstruktor menerima nilai awal dan opsi
- Nilai dibaca dengan
get()dan diubah denganset(t)
Signal.Computed<T>adalah Signal terhitung yang berbasis pada Signal lain- Dihitung dari nilai yang dikembalikan callback
- Melacak dependensi secara otomatis
- Nilainya dihitung secara lazy dan disimpan dalam cache
Signal.subtlememuat API tingkat lanjut yang lebih dekat ke penulis framework atau implementasi DevToolsuntrack(cb)membaca Signal tanpa pelacakancurrentComputed()mengembalikan computed Signal yang sedang dilacak saat iniintrospectSources,introspectSinks,hasSinks,hasSourcesadalah API untuk mengamati grafWatcheradalah fondasi untuk mendeteksi perubahan Signal dan mengimplementasikan effect serta scheduling di level framework
SignalOptions<T>mendukung fungsi pembanding kustomequalsserta hookwatched/unwatched
Cara kerja dan model eksekusi
- Signal merepresentasikan sel data yang dapat berubah seiring waktu, dan dibagi menjadi
stateataucomputed - Signal terhitung secara otomatis mencatat Signal yang dibaca saat eksekusi, lalu saat dibaca lagi memeriksa apakah dependensi sebelumnya telah berubah
- Komputasi bersifat pull-based
- Tidak langsung dihitung ulang ketika dependensi berubah
- Dihitung ulang hanya bila perlu saat seseorang membacanya lewat
.get()
- Penulisan ke State Signal tercermin secara sinkron
- Setelah
.set(), jika computed Signal yang bergantung padanya dibaca, ia akan langsung dihitung ulang bila perlu - Tidak ada batching bawaan
- Setelah
- Callback
notifymilik Watcher dapat dijalankan secara sinkron selama.set()- Namun Signal tidak boleh dibaca atau ditulis selama
notify - Operasi baca/tulis yang sebenarnya harus dijadwalkan setelahnya
- Namun Signal tidak boleh dibaca atau ditulis selama
- Jika callback computed Signal melempar exception, exception tersebut juga disimpan dalam cache seperti nilai, dan akan dilempar lagi saat Signal dibaca kembali
Motivasi standardisasi
- Implementasi Signal di tiap framework memiliki mekanisme pelacakan otomatisnya sendiri, sehingga model, komponen, dan library sulit dibagikan antar-framework
- Tujuan proposal ini adalah memisahkan model reaktif dari view rendering
- Agar pengembang tidak perlu menulis ulang kode non-UI meskipun teknologi rendering diganti
- Agar model reaktif yang dapat dibagikan di berbagai konteks bisa dibuat di JavaScript
- Dari sisi performa dan memori, implementasi bawaan mungkin lebih efisien daripada implementasi JS dengan faktor konstan yang lebih kecil, tetapi dijelaskan bahwa engine tidak akan secara ajaib mengubah algoritmenya
- Dari sisi DevTools, Signals bawaan dapat menampilkan informasi berikut dengan lebih baik
- call stack dari rantai computed Signal
- graf referensi antar-Signal
- hubungan dependensi yang diperlukan untuk debugging penggunaan memori
- Jika dimasukkan ke pustaka standar, efek samping positif yang diharapkan juga mencakup pengurangan ukuran bundle, peningkatan stabilitas dan kualitas, serta pembentukan kosakata bersama antar-proyek
Tujuan dan batasan desain
- Fitur inti mencakup Signal yang dapat ditulis, Signal terhitung, reaksi terhadap status dirty, scheduling framework itu sendiri,
untrack, dan komposisi lintas berbagai codebase - Signal terhitung menargetkan eksekusi bebas glitch
- Untuk menghindari komputasi yang tidak perlu, bagian graf yang berpotensi dirty dijalankan dengan pengurutan topologis
- Komputasi duplikat diupayakan untuk dihilangkan
- Tidak disertakan scheduling bawaan yang dipaksakan ala Promise agar framework bisa melakukan scheduling sendiri
- Untuk mencegah penyalahgunaan callback reaksi sinkron, pembacaan dan penulisan Signal dilarang di dalam
notifymilik Watcher untrackdiperlakukan sebagai jalur keluar yang tidak aman- Jika Signal yang dibaca tanpa pelacakan memengaruhi hasil komputasi, maka saat Signal itu berubah, computed Signal mungkin tidak ikut diperbarui
- API ini diprioritaskan sebagai fondasi implementasi framework, dan tidak secara khusus dirancang agar ergonomis bagi pengembang aplikasi umum
effect dan Watcher
- Proposal ini tidak memiliki fungsi bawaan seperti
effect() - Scheduling effect terkait erat dengan siklus rendering framework, disposal, dan manajemen kepemilikan, sehingga API standar JavaScript tidak menanganinya secara langsung
- Sebagai gantinya,
Signal.subtle.Watchermenyediakan fondasi tingkat rendah untuk implementasi effectnotifydipanggil ketika dependensi dari Signal yang diawasi berubahgetPending()dapat digunakan untuk memeriksa Signal yang masih dirty- Effect yang perlu di-dispose harus dibersihkan dengan
unwatch
- Signal yang sedang dipantau oleh Watcher dapat tetap hidup selama state internalnya masih dapat dijangkau, sehingga saat membersihkan effect diperlukan pemanggilan
Watcher.prototype.unwatch
Fitur yang belum masuk dalam draf saat ini
- Async belum termasuk dalam model saat ini
- Signals selalu diperlakukan sebagai nilai yang dapat dievaluasi secara sinkron
- Ada sebagian cara untuk memodelkan status loading sebagai exception, dan pembahasan perbaikannya ada di Issue #30
- Transactions juga belum termasuk
- Untuk mempertahankan status “from” dan “to” secara bersamaan dalam transisi layar, muncul masalah forking pada status graf Signal
- Pembahasan terkait ada di Issue #73
- Beberapa convenience methods juga belum ada dalam draf saat ini
- Fitur-fitur yang belum masuk dikecualikan karena kurangnya konsensus antar-framework dan karena masih bisa diakali di lapisan yang lebih tinggi, tetapi dapat ditinjau ulang setelah prototipe tersedia
Rencana pengembangan dan jadwal standardisasi
- Proposal ini masuk ke agenda TC39 Stage 1 pada April 2024, tetapi dijelaskan bahwa secara dokumen saat ini juga bisa dipandang seperti Stage 0
- Sebelum mengusulkan Stage 2, pekerjaan berikut direncanakan
- Mengembangkan beberapa implementasi polyfill kelas produksi
- Lulus berbagai pengujian framework dan pengujian bergaya test262
- Memverifikasi performa melalui benchmark set signal/framework yang menyeluruh
- Mengintegrasikan API proposal ke berbagai framework JS representatif dan sebagian aplikasi skala besar
- Memahami kemungkinan perluasan API dan memutuskan apakah akan disertakan
- Kelompok proposal ingin bergerak secara konservatif agar bentuk Signals yang keliru tidak terlalu cepat distandardisasi
- FAQ memperkirakan bahwa Signals standar akan membutuhkan setidaknya 2~3 tahun sebelum dapat digunakan secara luas di browser tanpa polyfill
- Polyfill saat ini sudah bisa digunakan, tetapi disarankan untuk tidak bergantung pada kestabilannya karena API dapat berubah selama proses peninjauan
Model penggunaan yang dirangkum di FAQ
- Signals bawaan bersifat independen dari teknologi rendering
- Dijelaskan bahwa semuanya memungkinkan, baik Preact yang memakai VDOM, Solid yang memakai native DOM, maupun Vue yang memakai pendekatan campuran
- Untuk pengembang aplikasi, penggunaan Signals melalui framework pada umumnya lebih cocok
- Framework mengelola Watcher,
untrack, ownership, disposal, dan scheduling rendering DOM
- Framework mengelola Watcher,
- Dapat digunakan bersama SSR, hydration, dan resumability
- Qwik sudah menggunakan Signals bersama karakteristik ini, dan pihak proposal menilai bahwa resumable Signals milik Qwik dapat dimodelkan dengan kombinasi State dan Computed
- Signals dan
Proxysaling melengkapi- Proxy mencegat operasi objek dangkal, sementara Signals mengoordinasikan graf dependensi sel data
- Menempatkan Signals di balik Proxy dapat membuat struktur reaktif bertingkat menjadi lebih ergonomis
- Signals bukan stream, melainkan sel yang merepresentasikan nilai saat ini
- Jika dua kali penulisan dilakukan berturut-turut ke State Signal lalu tidak ada tindakan lain, penulisan pertama mungkin tidak akan terlihat oleh computed Signal atau effect
- Dokumen menyebut ini sebagai karakteristik di sisi berlawanan dari eksekusi bebas glitch, dan menyatakan bahwa untuk stream, konstruksi lain seperti async iterable atau observable lebih cocok
1 komentar
Komentar Hacker News
Apakah hanya saya yang merasa contoh JavaScript murni justru lebih mudah dibaca dan ditangani?
Katanya “konfigurasinya berisik dan banyak boilerplate”, tetapi contoh signals juga tampak sama berisik dan penuh boilerplate, bahkan menambahkan konsep baru yang sulit dipahami pemula
Pernyataan bahwa “ketika counter berubah dari 2 ke 4, parity tidak berubah tetapi tetap melakukan komputasi dan rendering yang tidak perlu” terdengar seperti memoization prematur
Kalau bagian lain dari UI ingin me-render mengikuti pembaruan counter, benar bahwa contoh strawman itu tidak tepat, dan saat itu bisa memakai cara lain seperti signals, penanganan event, atau penyimpanan state terpusat (semacam Redux)
Kalau bagian lain dari UI hanya bergantung pada isEven atau parity, pendekatannya bisa diubah jika itu struktur inti aplikasi, tetapi kebanyakan tidak begitu. “Harus tahu bahwa fungsi render yang hanya bergantung pada parity perlu berlangganan ke counter” juga belum tentu beban yang tidak adil, dan fungsi komputasi murni punya keunggulan karena input-nya mudah dipahami
Upaya menstandarkan signals, sebuah konsep yang makin banyak dipakai dalam pengembangan UI, patut diapresiasi. Terlepas dari perdebatan detail seperti seberapa banyak boilerplate-nya atau apakah harus membuat sistem event sendiri, jika banyak framework memakai signals, mungkin ada alasannya, dan layak dicoba untuk distandardisasi meski memakan waktu
https://preactjs.com/guide/v10/signals
Di Preact, ketika signal turun melalui tree sebagai props atau context, yang diteruskan hanya referensi signal, dan karena komponen melihat signal, bukan nilainya, memperbarui signal bisa tidak me-render ulang komponen. Pada praktiknya, ini bisa langsung menuju komponen di tree yang mengakses
.valueSelain itu, signal melacak kapan nilai diakses dan kapan diperbarui, dan di Preact, jika
.valuedari sebuah signal diakses di dalam komponen, komponen itu otomatis di-render ulang ketika nilai signal tersebut berubahDari pengalaman, keuntungan besarnya adalah bisa memodularisasi state reaktif. Dalam gaya imperatif, state tambahan diperlukan untuk melacak perubahan, dan modularitas dicapai lewat abstraksi. Gunakan saja saat diperlukan
Membuat contoh yang sederhana sekaligus dapat diterapkan adalah soal keseimbangan. Kasus ketika reaktivitas jelas menguntungkan biasanya lebih kompleks, sehingga lebih sulit ditampilkan dibanding contoh yang sederhana tetapi kurang dapat diterapkan
Ketika Promises ditambahkan ke JavaScript, saya sempat enggan karena khawatir harus menulis
new Promisedi mana-manaKenyataannya, jumlah kali saya menulis
new Promiselangsung bisa dihitung dengan dua tangan. Sebaliknya, saya jauh lebih sering memakai.then, terutama saat menangani library pihak ketigaPada akhirnya, dampak sehari-hari Promise yang ditambahkan ke JavaScript adalah memberi antarmuka yang cukup sederhana, umumnya kokoh, dan hampir universal untuk berbagai perilaku dan fungsi khusus yang disediakan library pihak ketiga. Entah membaca file, request API, atau output tahap build, memakai
.then(res => …)membuat rasanya sudah setengah jalan menuju sesuatu yang bisa berjalanJika proposal Signal ini bisa memainkan peran serupa di tengah ledakan ala Kambrium framework UI reaktif, saya mendukungnya. Lebih jauh lagi, ini juga bisa membantu reaktivitas meluas ke luar UI. Saya sering membayangkan tree state rekalkulasi bertahap untuk hal-hal yang bukan state UI
new Promiselangsung.thenpada Promise awal adalah peningkatan besar dibanding delegate bertingkat dan cukup baik untuk chain sederhana, tetapi ketika perlu men-chain Promise yang berbeda secara kondisional, menangani error secara berbeda pada chain tertentu, atau melakukan return lebih awal, kodenya bisa menjadi jauh lebih sulit dibaca dan ditanganiDengan async/await, pemanggilan bisa ditulis seolah-olah bukan Promise, try/catch bisa dengan mudah diletakkan di sekitar pemanggilan Promise tertentu, dan return lebih awal juga terasa alami
Saya tidak paham mengapa ini harus menjadi bagian dari bahasa. Ini bisa dilakukan sebagai library, dan library seperti itu sudah ada. Karena kecil, memasukkannya ke dalam kode tidak terlalu membebani, dan menambahkannya ke bahasa tidak boleh menjadi tujuan itu sendiri
Menganggap bahwa library UI JS saat ini telah merancang signals dengan sangat baik hingga harus menjadi bagian dari bahasa adalah sikap arogan. Ada banyak implementasi signals dengan kompromi yang berbeda-beda, dan tidak satu pun dari mereka layak mendapat tempat khusus dalam spesifikasi JavaScript
Sebelum memakai signals, library-library seperti ini menggunakan virtual DOM. Untungnya virtual DOM tidak menjadi bagian dari JS, lalu apa bedanya signals? Tidak ada. Argumen untuk menstandarkannya bahkan lebih lemah dibanding saat virtual DOM
Apakah kita akan menumpuk semua hal yang sedang tren ke dalam runtime yang pada dasarnya tidak punya cara untuk menghapus fitur yang kelak tidak lagi diinginkan tanpa merusak web? Itu cukup rabun
UI reaktif sudah menang. Bahkan pada aplikasi kecil, ketika mengelola state, kompleksitas bisa meledak, dan itulah inti yang membuat JS murni sulit digunakan. Bagi saya, framework reaktif apa pun lebih baik daripada JS murni, jadi mungkin memang ada komponen yang hilang
Sekarang sudah sekitar 10 tahun berlalu, sudah saatnya memikirkan batas yang bisa distandarkan. Jika dilakukan dengan benar seperti Promise, ini bisa menurunkan kompleksitas untuk use case yang sangat umum
Penilaian yang lebih baik adalah bertanya, “Apakah framework reaktif yang ada akan memakai proposal ini?” Jika tidak, perlu dilihat mengapa, apa yang kurang, apa yang tidak perlu, dan apa yang bisa dipelajari dari UI serta reaktivitas di bahasa lain. Pengalaman yang tersebar layak disaring
Kebutuhan itu bisa diperdebatkan, tetapi jika standard library akan diperluas, melihat hal-hal yang populer menurut saya adalah pendekatan yang baik
signals bukan pengganti virtual DOM
Saat perlu memberi sinyal tentang sesuatu ke seluruh aplikasi, kita memakai event
window.dispatchEvent(new Event('counterChange'));Lalu bagian mana pun dari aplikasi yang ingin bereaksi bisa berlangganan seperti ini
window.addEventListener('counterChange', () => { ... do something ... });Apa yang salah dengan cara ini?
Penanganan event sangat mudah menjadi berantakan. Untuk melihat lebih dalam, lihat event bubbling dan propagation
Aplikasi besar membutuhkan penanganan event yang kokoh, dan inilah keunggulan framework seperti Angular dan Vue yang sekarang tidak terlalu terlihat
Anda mungkin tidak ingin memakai API penanganan event standar begitu saja tanpa framework. Saat menangani penambahan, penghapusan, duplikasi, pemicuan, pembersihan, pemicuan satu kali, dan sebagainya untuk banyak elemen, efek samping tak diinginkan yang serius bisa muncul
Perbedaannya dengan signals adalah nilai hasil baru dihitung hanya ketika konsumen akhir membaca nilainya. Waktu penulisan aktual ke signal dipisahkan dari penjadwalan pembaruan render asinkron, dan rantai komputasi yang dilakukan watcher hanya dijalankan sekali selama render
Nilai-nilai perantara yang dikirim lewat signal akan hilang, sehingga sulit melakukan banyak hal menarik di dalamnya; pada dasarnya ini lebih dekat ke lapisan abstraksi tingkat tinggi untuk mengoordinasikan siklus rendering
Performanya juga bisa lebih baik. Misalnya ada komputasi yang bergantung pada dua nilai: pada
result = a ? b : 0, jika a bernilai false, perubahan b tidak perlu memicu perhitungan ulang. Di signals, ini terjadi otomatis, sedangkan dengan publish/subscribe tradisional butuh cukup banyak kodeSulit juga menjamin bahwa semua listener tidak membuat pemicuan berantai seperti itu
Selama puluhan tahun, orang-orang berusaha memahami mengapa pelacakan state dan pembaruan DOM terasa begitu sulit
Tentu perlu sedikit disiplin, tetapi rasanya jauh lebih sederhana dibandingkan solusi-solusi yang muncul setiap beberapa tahun. Backbone, Knockout, Angular, React, modifikasi bahasa itu sendiri, dan sebagainya; mungkin cara berpikirku memang pada dasarnya berbeda
Bahkan terlihat dari nama fungsinya. Memperbarui
innerTextdisebut “render”, padahal sebenarnya bukan merender. Paling-paling browser yang merender, dan itu juga berlaku untuk semua pekerjaan lain yang terkait painting. Rasanya seperti upaya putus asa untuk membuat salah satu fungsi DOM paling sederhana menjadi rumit, jadi benar-benar membingungkanKalau makin kompleks, tidak mudah
Kemajuan pemrograman bisa dilihat sebagai proses menghilangkan ritual dan disiplin ketat yang diperlukan untuk mendapatkan hasil yang baik
Bukan berarti React adalah evolusi berikutnya, tetapi signals jelas merupakan satu langkah ke arah yang benar
Menyinkronkan DOM dengan state data itu sendiri tidak terlalu sulit, tetapi melakukannya dengan performa sangat baik pada 60fps luar biasa sulit. Terutama saat membuat API yang tidak bocor tetapi juga tidak terlalu merepotkan
Sejujurnya, menggambar piksel di canvas seperti game bisa jadi lebih mudah daripada menerjemahkan perubahan lalu mencerminkannya ke pohon DOM yang hidup
Promises adalah contoh sukses yang bagus, tetapi tanpa async/await, belum tentu perlu distandardisasi
Draf saat ini katanya didasarkan pada desain dari para pembuat/pemelihara Angular, Bubble, Ember, FAST, MobX, Preact, Qwik, RxJS, Solid, Starbeam, Svelte, Vue, Wiz, dan lainnya; saya penasaran bagaimana para pembuat library yang sudah ada melihat proposal ini. Menarik juga bahwa React tidak ada dalam daftar
Signals agak mirip channel, tetapi bedanya ini broadcast, bukan penerima tunggal. Akan keren jika ini dimanfaatkan agar web worker bisa berkomunikasi lewat channel alih-alih callback
onMessage. Terutama jika, seperti Go, kita bisa melakukanselectdi atas signals/channels/promises; secara sintaksis itu punya keunggulan dibanding mengelola beberapa mekanisme messaging konkuren dengan callback. Misalnya dengan memungkinkan signals disertakan dalamPromise.anyx instanceof Promisesama sekali tidak berfungsi begitu saja. Jika metodethendi library saya menerima callback catch tetapi library lain tidak, keduanya diam-diam tidak interoperabel dan tidak ada cara untuk mendeteksinya. Kapanfinallydijalankan? Ekspektasi apa yang bisa kita punya tentang seberapa asinkron callback akan dieksekusi?Tanpa standar, setiap library yang menggunakan Promise harus membawa polyfill-nya sendiri. Karena kita tidak bisa mempercayai yang sudah ada. Dan Promise dari library lain pun tidak benar-benar bisa dikonsumsi. Karena kita tidak bisa yakin ia akan berperilaku sesuai harapan
Ini bukan dugaan; selama bertahun-tahun memang begitulah keadaannya, dan itu adalah neraka yang harus ditanggung banyak orang
Intuisi samar saya, signals terlalu mirip dengan
useEffect()yang digeneralisasi, sehingga jika masuk ke React, itu akan membuat apa yang terjadi selama siklus render menjadi lebih membingungkan. Baik atau buruk, React memilih cara pembaruan yang berbeda dari signals. Namun saya bisa saja keliru soal kemungkinan penerapannyauseEffectmengisolasi perilaku imperatif dengan rapiIni terlihat sangat mirip dengan data binding Ember, dan pada akhirnya bisa menjadi mimpi buruk imperatif. Keadaan default-nya nyaris seperti “senjata yang menembak kaki sendiri”, dan untuk mencegahnya menjadi seperti itu dibutuhkan beban kognitif dan meta-pattern yang sangat besar
https://nodejs.org/en/learn/asynchronous-work/the-nodejs-eve...
Tidak memahami contoh di README yang ditautkan
// A library or framework defines effects based on other Signal primitivesdeclare function effect(cb: () => void): (() => void);Library apa? Framework apa? Saya tersesat di sini. Apa itu
effect?effect(() => element.innerText = parity.get());Bagaimana
effecttahu bahwa ia harus memanggil lambda ini setiap kali parity berubah? Apakah ia memanggil lambda ini setiap kali signal berubah? Kalau begitu, mengapa membahas caching? Mungkin bukan begituBagaimanapun, jika saya memahami dengan benar apa yang ingin disampaikan para penulis, ide signal itu sendiri terlihat masuk akal. Namun masalah besar dari arsitektur terpisah seperti ini adalah, ketika aplikasi menjadi cukup kompleks, kita akan tersesat saat mencoba melacak mengapa event tertentu terjadi. Idealnya, signals harus memperbaiki stack trace sehingga saat callback dipanggil, stack trace dari kode yang sejak awal memicu signal sudah ikut disertakan
effect, dan memungkinkan kode arbitrer dijalankan sebagai respons terhadap update signal. Pengantar signals dan effects di dokumentasi Preact bagus: https://preactjs.com/guide/v10/signals#effectfnSejauh yang saya pahami, fungsi effect semacam ini pertama-tama menjalankan callback sekali untuk mengetahui signal apa saja yang diakses selama eksekusi, lalu memanggil ulang callback setiap kali signal yang menjadi dependensi callback itu diperbarui. Jika akses signal bersifat sinkron dan single-threaded, fakta bahwa sebuah signal diakses saat callback berjalan sudah cukup untuk mengetahui bahwa callback tersebut harus berlangganan ke signal itu
Ini juga bisa dilakukan dengan getter. Fungsi effect melacak properti signal mana yang diakses di dalam metode getter, dan setahu saya Vue 2 dulu memakai pendekatan seperti ini. Akses objek juga bisa dilacak dengan proxy. Contoh dalam proposal memiliki metode
getyang dipanggil untuk mengakses nilai signal, dan dependensi dapat dilacak melalui eksekusi metode ini[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
parity.get()mendaftarkan dependensi untuk fungsi yang diteruskan keeffect(). Jikaparitydiperbarui, fungsi itu akan dipanggilBukan dipanggil setiap kali signal berubah, melainkan hanya saat signal yang menjadi dependensinya berubah
Dalam kasus ini
paritybergantung padaisEven, danisEvenbergantung padacounter. Jadi ketikacounterdiperbarui, seluruh rantai dependensi diinvalidasi,paritymenjadi invalid, dan callback dijalankan lagieffecthipotetis ini, pembacaan membuat edge antara node status signal dan node komputasi effect, yang pada dasarnya membuat yang terakhir berlangganan ke penulisan berikutnya pada yang pertama, sehingga sistem dapat menentukan kapan komputasi perlu dijalankan ulangeffectadalah fungsi arbitrer yang ingin Anda panggilDalam signals, mekanisme pelacakan dependensi mengetahui nilai mana yang perlu dihitung ulang, dan sebagai hasilnya sistem juga mengetahui fungsi mana yang harus dipanggil ulang
effectTerkait ini ada S.js: https://github.com/adamhaile/s
Saya suka signals. Saat membuat UI, saya lebih memilihnya daripada primitive lain mana pun, mungkin dengan pengecualian algoritma constraint cassowary. Saya mencoba meniru signals di semua bahasa yang saya pakai untuk bersenang-senang
Namun saya sama sekali tidak berpikir ini sesuatu yang harus masuk ke bahasa JavaScript itu sendiri. Saya berharap bahasa ini dibiarkan saja untuk sementara. Orang-orang sudah kesulitan mengikutinya, dan TC-39 sudah membuat orang merasa terintimidasi lalu menjauh dari bahasa ini
Ini terlihat sangat mirip dengan sistem effect JS favorit saya, MobX
Versi MobX seperti ini
import { observable, computed, autorun } from 'mobx';const counter = observable.box(0);const isEven = computed(() => (counter.get() & 1) === 0);const parity = computed(() => isEven.get() ? "even" : "odd");autorun(() => { element.innerText = parity.get(); });setInterval(() => counter.set(counter.get() + 1), 1000);Rasanya seperti “ayo masukkan framework yang sedang kupakai belakangan ini ke standard library!”
Mirip seperti menato nama pacar di tubuh
Ini memasukkan komponen penyusun yang sudah menjadi titik konvergensi dan digunakan oleh sebagian besar framework ke standard library
Promises juga masuk ke standard library setelah dipakai secara luas, dan ini mirip dengan itu