JavaScript Signals मानक प्रस्ताव का प्रारूप
- यह JavaScript में signals के लिए शुरुआती साझा दिशा को समझाने वाला दस्तावेज़ है, जो ES2015 में TC39 द्वारा मानकीकृत किए जाने से पहले Promises/A+ के प्रयासों जैसा है।
- यह प्रयास JavaScript ecosystem में समन्वय पर केंद्रित है, और यदि यह समन्वय सफल होता है, तो उस अनुभव के आधार पर आगे चलकर मानक उभर सकता है।
- कई framework लेखक ऐसे common model पर सहयोग कर रहे हैं जो reactivity core को समर्थन दे सके।
- मौजूदा draft Angular, Bubble, Ember, FAST, MobX, Preact, Qwik, RxJS, Solid, Starbeam, Svelte, Vue, Wiz आदि के लेखकों/maintainers से मिले design input पर आधारित है।
पृष्ठभूमि: Signals क्यों?
- जटिल user interface (UI) बनाने के लिए JavaScript application developers को state को store, calculate, invalidate, sync और application की view layer तक efficient तरीके से push करना पड़ता है।
- UI में सिर्फ simple values को manage करना ही नहीं, बल्कि अक्सर ऐसे computed state को render करना भी शामिल होता है जो दूसरी values या state पर निर्भर हो।
- Signals का लक्ष्य ऐसे application state को manage करने के लिए infrastructure देना है, ताकि developers दोहराए जाने वाले विवरणों के बजाय business logic पर ध्यान दे सकें।
उदाहरण - VanillaJS काउंटर
- मान लें
counter नाम का एक variable है, और हर बार इसके बदलने पर DOM में काउंटर की even/odd स्थिति अपडेट करनी है।
- Vanilla JS में कोड कुछ ऐसा हो सकता है:
let counter = 0;
const setCounter = (value) => {
counter = value;
render();
};
const isEven = () => (counter & 1) == 0;
const parity = () => isEven() ? "even" : "odd";
const render = () => element.innerText = parity();
// Simulate external updates to counter...
setInterval(() => setCounter(counter + 1), 1000);
- इस कोड में कुछ समस्याएँ हैं:
counter को set करना शोरभरा है और इसमें boilerplate ज़्यादा है।
counter state rendering system से काफ़ी tightly coupled है।
- जब
counter बदलता है लेकिन parity नहीं बदलती (जैसे 2 से 4), तब भी अनावश्यक computation और rendering होती है।
- अगर UI का कोई दूसरा हिस्सा सिर्फ
counter update होने पर ही render होना चाहता हो।
- UI के वे दूसरे हिस्से जो सिर्फ
isEven या parity पर निर्भर हैं, वे counter के साथ सीधे interact किए बिना update नहीं हो सकते।
Signals का परिचय
- model और view के बीच data binding abstraction लंबे समय से UI frameworks का मुख्य हिस्सा रही है, भले ही JS या web platform में ऐसा mechanism built-in न हो।
- JS frameworks और libraries में ऐसी binding को व्यक्त करने के कई तरीकों पर बहुत प्रयोग हुए हैं, और state या दूसरे data से निकली computations को first-class reactive values के रूप में दिखाने वाले approach, जिसे अक्सर "Signals" कहा जाता है, की ताकत साबित हो चुकी है।
- ऊपर वाले उदाहरण को signal API के साथ फिर से सोचें, तो वह इस तरह दिख सकता है:
const counter = new Signal.State(0);
const isEven = new Signal.Computed(() => (counter.get() & 1) == 0);
const parity = new Signal.Computed(() => isEven.get() ? "even" : "odd");
// A library or framework defines effects based on other Signal primitives
declare function effect(cb: () => void): (() => void);
effect(() => element.innerText = parity.get());
// Simulate external updates to counter...
setInterval(() => counter.set(counter.get() + 1), 1000);
Signals के मानकीकरण की प्रेरणा
Interoperability
- हर signal implementation का अपना auto-tracking mechanism होता है, इसलिए अलग-अलग frameworks के बीच model, component और library साझा करना मुश्किल होता है।
- इस प्रस्ताव का लक्ष्य reactive model को rendering view से पूरी तरह अलग करना है, ताकि developers नई rendering technology पर जाने पर non-UI code दोबारा लिखने से बच सकें, या ऐसे shared reactive model को JS में विकसित कर सकें जिन्हें अलग-अलग contexts में deploy किया जा सके।
Performance/Memory उपयोग
- आम तौर पर इस्तेमाल होने वाली library के built-in होने से कम code भेजना हमेशा थोड़ा संभावित performance लाभ दे सकता है, लेकिन signal implementations आम तौर पर काफ़ी छोटे होते हैं, इसलिए इस प्रभाव के बहुत बड़ा होने की उम्मीद नहीं है।
Developer tools
- मौजूदा JS language signal libraries का उपयोग करते समय computed signal chain के call stack, signals के बीच reference graph आदि को track करना मुश्किल होता है।
- Built-in signals, JS runtime और developer tools को signals की जाँच के लिए बेहतर support देने में मदद कर सकते हैं।
अतिरिक्त लाभ
Standard library के फायदे
- सामान्यतः JavaScript की standard library काफ़ी minimal रही है, लेकिन TC39 का रुझान JS को एक ऐसी "batteries included" language बनाने की ओर है जिसमें high-quality built-in features का सेट हो।
HTML/DOM integration (भविष्य की संभावना)
- W3C और browser implementers इस समय HTML में native templates लाने पर काम कर रहे हैं।
- इन लक्ष्यों को हासिल करने के लिए अंततः HTML में reactive primitives की ज़रूरत होगी।
Signal design के लक्ष्य
- मौजूदा signal libraries अपने core में बहुत अलग नहीं हैं।
- यह प्रस्ताव कई libraries की महत्वपूर्ण विशेषताओं को लागू करके उनकी सफलता पर आगे निर्माण करना चाहता है।
मुख्य सुविधाएँ
- state को दर्शाने वाला Signal type, यानी writable Signal।
- दूसरा Signal type जो अन्य signals पर निर्भर हो, lazily compute हो और cache किया जाए — computed/memo/derived Signal।
- JS frameworks को अपनी scheduling करने की सुविधा देना।
API रूपरेखा
- शुरुआती signal API का विचार नीचे जैसा है। यह सिर्फ एक शुरुआती draft है और समय के साथ इसमें बदलाव होने की उम्मीद है।
namespace Signal {
// A read-write Signal
class State<T> implements Signal<T> {
// Create a state Signal starting with the value t
constructor(t: T, options?: SignalOptions<T>);
// Get the value of the signal
get(): T;
// Set the state Signal value to t
set(t: T): void;
}
// A Signal which is a formula based on other Signals
class Computed<T> implements Signal<T> {
// Create a Signal which evaluates to the value returned by the callback.
// Callback is called with this signal as the this value.
constructor(cb: (this: Computed<T>) => T, options?: SignalOptions<T>);
// Get the value of the signal
get(): T;
}
// This namespace includes "advanced" features that are better to
// leave for framework authors rather than application developers.
// Analogous to `crypto.subtle`
namespace subtle {
// Run a callback with all tracking disabled (even for nested computed).
function untrack<T>(cb: () => T): T;
// Get the current computed signal which is tracking any signal reads, if any
function currentComputed(): Computed | null;
// Returns ordered list of all signals which this one referenced
// during the last time it was evaluated.
// For a Watcher, lists the set of signals which it is watching.
function introspectSources(s: Computed | Watcher): (State | Computed)[];
// Returns the Watchers that this signal is contained in, plus any
// Computed signals which read this signal last time they were evaluated,
// if that computed signal is (recursively) watched.
function introspectSinks(s: State | Computed): (Computed | Watcher)[];
// True if this signal is "live", in that it is watched by a Watcher,
// or it is read by a Computed signal which is (recursively) live.
function hasSinks(s: State | Computed): boolean;
// True if this element is "reactive", in that it depends
// on some other signal. A Computed where hasSources is false
// will always return the same constant.
function hasSources(s: Computed | Watcher): boolean;
class Watcher {
// When a (recursive) source of Watcher is written to, call this callback,
// if it hasn't already been called since the last `watch` call.
// No signals may be read or written during the notify.
constructor(notify: (this: Watcher) => void);
// Add these signals to the Watcher's set, and set the watcher to run its
// notify callback next time any signal in the set (or one of its dependencies) changes.
// Can be called with no arguments just to reset the "notified" state, so that
// the notify callback will be invoked again.
watch(...s: Signal[]): void;
// Remove these signals from the watched set (e.g., for an effect which is disposed)
unwatch(...s: Signal[]): void;
// Returns the set of sources in the Watcher's set which are still dirty, or is a computed signal
// with a source which is dirty or pending and hasn't yet been re-evaluated
getPending(): Signal[];
}
// Hooks to observe being watched or no longer watched
var watched: Symbol;
var unwatched: Symbol;
}
interface Options<T> {
// Custom comparison function between old and new value. Default: Object.is.
// The signal is passed in as the this value for context.
equals?: (this: Signal<T>, t: T, t2: T) => boolean;
// Callback called when isWatched becomes true, if it was previously false
[Signal.subtle.watched]?: (this: Signal<T>) => void;
// Callback called whenever isWatched becomes false, if it was previously true
[Signal.subtle.unwatched]?: (this: Signal<T>) => void;
}
}
Signal एल्गोरिद्म
- इसमें हर उस API के लिए implementation algorithm समझाया गया है जो JavaScript को expose किया जाएगा।
- इसे शुरुआती specification माना जा सकता है, और इसका उद्देश्य, बदलाव के लिए खुले होने के बावजूद, semantics के एक संभावित set को यथासंभव स्पष्ट करना है।
GN⁺ की राय
- JavaScript Signals मानक प्रस्ताव का लक्ष्य frameworks के बीच interoperability को बेहतर बनाना और developers के लिए reactive programming को लागू करना आसान बनाना है।
- यह प्रस्ताव मौजूदा कई signal libraries की core functionality को standardize करने की कोशिश है, जिससे developers को एक consistent programming model मिल सकता है।
- Signals की अवधारणा सिर्फ UI development ही नहीं, बल्कि non-UI context में भी उपयोगी हो सकती है, खासकर build systems में अनावश्यक rebuild से बचने के लिए।
- प्रस्तावित API framework developers को उपयोगी tools दे सकता है, जिनकी मदद से बेहतर performance और memory management हासिल किया जा सकता है।
- लेकिन इस तकनीक को व्यापक रूप से अपनाए जाने के लिए और अधिक prototyping तथा community feedback की ज़रूरत होगी, और इसे वास्तविक applications में integrate करके इसकी उपयोगिता साबित करनी होगी।
- React, Vue, Svelte जैसे frameworks के पास पहले से अपने reactive systems हैं, इसलिए उनके साथ compatibility या integration strategy भी एक महत्वपूर्ण विचार होगा।
1 टिप्पणियां
Hacker News राय
Vanilla JS बनाम Signals उदाहरण
isEvenया parity पर निर्भर करते हैं, तो पूरे approach को बदलने की ज़रूरत पड़ सकती है।Promises और JavaScript में बदलाव
new Promiseबहुत बार न लिखना पड़े, लेकिन व्यवहार में इसका लगभग कभी इस्तेमाल नहीं करना पड़ा।.thenका काफ़ी उपयोग हुआ, और इससे अलग-अलग third-party libraries के साथ interface सरल हो गया।भाषा के हिस्से के रूप में Signals
एप्लिकेशन में events का उपयोग
window.dispatchEventऔरwindow.addEventListenerके ज़रिए events emit और subscribe करता हूँ।DOM state management और updates की कठिनाई
Promises और asynchronous programming
S.js और Signals
MobX जैसे Signals
Standard library में framework जोड़ना
Signal प्रस्ताव की समझ और समस्याएँ
effectफ़ंक्शन parity में बदलाव को कैसे detect करता है, और क्या यह किसी भी signal change पर इस lambda को call करता है।