3 पॉइंट द्वारा GN⁺ 2024-04-01 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • TC39 का JavaScript Signals प्रस्ताव UI state और computed state को कुशलता से ट्रैक करने के लिए reactive primitive values को standardize करने की शुरुआती दिशा है, और फिलहाल Stage 1 स्तर का draft है
  • यह प्रस्ताव application developers के सीधे उपयोग वाले surface API से अधिक उन Signal graph की core semantics और automatic tracking mechanism पर केंद्रित है जिन्हें frameworks आपस में साझा कर सकें
  • Signal.State, Signal.Computed, Signal.subtle.Watcher इसके मुख्य API हैं, और computations का लक्ष्य lazy evaluation, caching, automatic dependency tracking, और glitch-free execution है
  • built-in Signals का लक्ष्य Angular, Ember, MobX, Preact, Qwik, Solid, Svelte, Vue जैसे कई frameworks के बीच interoperability बढ़ाना और DevTools में debugging तथा performance analysis support की संभावना खोलना है
  • प्रस्ताव समूह Stage 2 से पहले कई production-grade polyfill, framework integration, large-scale application validation, और performance benchmark से गुजरना चाहता है, और standardization तक पहुंचने में कम से कम 2~3 साल या उससे अधिक लग सकते हैं

JavaScript Signals प्रस्ताव की स्थिति

  • JavaScript Signals को TC39 process के Stage 1 प्रस्ताव के रूप में पेश किया गया है
  • मौजूदा दस्तावेज़ ES2015 में Promise के standardize होने से पहले के Promises/A+ की तरह, JavaScript ecosystem के लिए एक साझा दिशा तय करने की कोशिश है
  • सीधे प्रयोग के लिए एक polyfill उपलब्ध है
  • प्रस्ताव के champions और मूल लेखक ने कई frameworks और libraries के design input के आधार पर मौजूदा draft तैयार किया है

standardization किस समस्या को निशाना बनाती है

  • जटिल UI को values को store, compute, invalidate, synchronize और view layer तक push करना पड़ता है, और Signals इस तरह के बार-बार होने वाले काम के लिए state management infrastructure देना चाहता है
  • Vanilla JS उदाहरण में counter, isEven, parity, render सीधे एक-दूसरे में उलझे रहते हैं और इससे ये समस्याएं पैदा होती हैं
    • state और rendering system के बीच कड़ा coupling होता है
    • counter के 2 से 4 होने जैसे मामलों में, parity न बदलने पर भी अनावश्यक computation और rendering होती है
    • जब UI के अलग हिस्से counter, isEven, parity में से सिर्फ कुछ को subscribe करना चाहते हैं, तब manual subscription और cleanup प्रबंधन जटिल हो जाता है
    • कई स्तरों पर pub/sub जोड़ने से boilerplate और subscription bookkeeping बढ़ती है और memory leak का जोखिम बनता है
  • Signals आधारित उदाहरण Signal.State और Signal.Computed का उपयोग करके values, computations और side effects को एक ही तरीके से संभालता है
    • manual subscription की जरूरत नहीं होती
    • computed Signal अपने dependencies वाले Signal को अपने-आप खोज लेता है
    • computation सिर्फ तब चलती है जब value को स्पष्ट रूप से मांगा जाए
    • computed Signal अपनी पिछली value को cache करता है

प्रस्ताव के core API

  • Signal<T> को get(): T वाले एक readable value के रूप में परिभाषित किया गया है
  • Signal.State<T> एक writable Signal है
    • constructor initial value और options लेता है
    • get() से value पढ़ी जाती है और set(t) से बदली जाती है
  • Signal.Computed<T> दूसरे Signal पर आधारित एक computed Signal है
    • callback जो value लौटाता है, उसी से इसकी computation होती है
    • dependencies अपने-आप track होती हैं
    • value lazy तरीके से compute और cache होती है
  • Signal.subtle में framework authors या DevTools implementation के करीब आने वाले advanced API शामिल हैं
    • untrack(cb) tracking के बिना Signal पढ़ने देता है
    • currentComputed() अभी track हो रहे computed Signal को लौटाता है
    • introspectSources, introspectSinks, hasSinks, hasSources graph को देखने के API हैं
    • Watcher Signal changes को detect करके framework-level effect और scheduling को implement करने का आधार है
  • SignalOptions<T> custom comparison function equals और watched / unwatched hooks को support करता है

काम करने का तरीका और execution model

  • Signal समय के साथ बदल सकने वाले data cell को दर्शाता है, और यह state या computed में बंटा होता है
  • computed Signal execution के दौरान पढ़े गए Signal को अपने-आप रिकॉर्ड करता है, और बाद में पढ़े जाने पर जांचता है कि पिछली dependencies बदली हैं या नहीं
  • computation pull-based है
    • dependency बदलने पर तुरंत recompute नहीं होता
    • जब कोई .get() से पढ़ता है, तभी जरूरत होने पर recompute होता है
  • State Signal में write synchronous रूप से लागू होती है
    • .set() के बाद यदि उस value पर निर्भर computed Signal पढ़ा जाए, तो जरूरत पड़ने पर वह तुरंत recompute होता है
    • built-in batching नहीं है
  • Watcher का notify callback .set() के दौरान synchronous रूप से चल सकता है
    • लेकिन notify के दौरान Signal को पढ़ा या लिखा नहीं जा सकता
    • असली read/write काम बाद में schedule करना होगा
  • यदि computed Signal का callback exception throw करता है, तो वह exception भी value की तरह cache हो जाता है, और Signal को दोबारा पढ़ने पर फिर से throw किया जाता है

standardization की प्रेरणा

  • हर framework का Signal implementation अपना automatic tracking mechanism रखता है, इसलिए model, component और library को frameworks के बीच साझा करना कठिन होता है
  • प्रस्ताव का लक्ष्य reactive model को rendering view से अलग करना है
    • ताकि developers rendering technology बदलें तब भी non-UI code दोबारा न लिखना पड़े
    • और JavaScript में ऐसे reactive model बनाए जा सकें जिन्हें कई contexts में साझा किया जा सके
  • performance और memory के लिहाज से built-in implementation, JS implementation की तुलना में छोटे constant factor तक अधिक efficient हो सकता है, लेकिन यह साफ कहा गया है कि engine कोई जादुई algorithmic बदलाव नहीं करेगा
  • DevTools के संदर्भ में built-in Signals निम्न जानकारी को बेहतर तरीके से दिखा सकते हैं
    • computed Signal chain का call stack
    • Signals के बीच reference graph
    • memory usage debugging के लिए जरूरी dependency relations
  • अगर इसे standard library में शामिल किया जाता है, तो bundle size कम होने, stability और quality बेहतर होने, और projects के बीच common vocabulary बनने जैसे अतिरिक्त प्रभाव भी अपेक्षित हैं

design goals और constraints

  • core capabilities में writable Signal, computed Signal, dirty state पर reaction, framework-controlled scheduling, untrack, और कई codebase की composition शामिल हैं
  • computed Signal का लक्ष्य glitch-free होना है
    • अनावश्यक computation से बचने के लिए graph के संभावित dirty हिस्सों को topological order में चलाया जाता है
    • duplicate computation हटाने की कोशिश की जाती है
  • frameworks अपनी scheduling खुद कर सकें, इसलिए Promise-style built-in forced scheduling नहीं जोड़ी गई है
  • synchronous reaction callback के दुरुपयोग को रोकने के लिए Watcher के notify के अंदर Signal read और write पर रोक है
  • untrack को एक unsafe escape hatch माना गया है
    • अगर tracking के बिना पढ़ा गया Signal computation result को प्रभावित करता है, तो उस Signal में बदलाव होने पर computed Signal update नहीं हो सकता
  • API को प्राथमिकता से framework implementation की बुनियाद बनने के लिए डिजाइन किया गया है, आम app developers के लिए इसे खास तौर पर ergonomic नहीं बनाया गया है

effect और Watcher

  • प्रस्ताव में effect() जैसी कोई built-in function नहीं है
  • effect scheduling framework के rendering cycle, disposal और ownership management से जुड़ी होती है, इसलिए JavaScript standard API इसे सीधे हल नहीं करती
  • इसके बजाय Signal.subtle.Watcher effect implementation के लिए low-level आधार देता है
    • watched Signal की dependencies बदलने पर notify कॉल होता है
    • getPending() से अभी भी dirty Signal की जांच की जा सकती है
    • जिन effect को dispose करना हो, उन्हें unwatch से साफ करना होगा
  • जिन Signal को Watcher देख रहा है, वे internal state reachable रहने तक जीवित रह सकते हैं, इसलिए effect cleanup के समय Watcher.prototype.unwatch कॉल करना जरूरी है

मौजूदा draft में शामिल नहीं की गई सुविधाएं

  • Async अभी इस model में शामिल नहीं है
    • Signals को हमेशा synchronous रूप से evaluate की जा सकने वाली values के रूप में माना जाता है
    • loading state को exception के रूप में model करने का एक सीमित तरीका संभव है, और सुधार पर चर्चा Issue #30 में है
  • Transactions भी शामिल नहीं हैं
    • screen transition में “from” state और “to” state को एक साथ बनाए रखने के लिए Signal graph state को fork करने की समस्या आती है
    • संबंधित चर्चा Issue #73 में है
  • कुछ convenience methods भी मौजूदा draft से बाहर हैं
  • जिन सुविधाओं को छोड़ा गया है, उन्हें frameworks के बीच पर्याप्त सहमति न होने और upper layer पर workaround संभव होने के कारण हटाया गया है, लेकिन prototype के बाद इन्हें फिर से देखा जा सकता है

development plan और standardization timeline

  • यह प्रस्ताव अप्रैल 2024 में TC39 Stage 1 agenda पर था, और दस्तावेज़ कहता है कि वर्तमान स्थिति को Stage 0 जैसा भी माना जा सकता है
  • Stage 2 प्रस्तावित करने से पहले निम्न काम की योजना है
    • कई production-grade polyfill implementation विकसित करना
    • विभिन्न framework tests और test262-style tests पास करना
    • thorough signal/framework benchmark set के जरिए performance validation करना
    • प्रतिनिधि JS frameworks और कुछ बड़े applications में प्रस्तावित API को integrate करना
    • API extensibility को समझना और क्या शामिल करना है यह तय करना
  • प्रस्ताव समूह सावधानी से आगे बढ़ना चाहता है ताकि Signals का कोई गलत रूप बहुत जल्दी standardize न हो जाए
  • FAQ के अनुसार standard Signals के polyfill के बिना ब्राउज़र भर में उपयोग होने लायक बनने में कम से कम 2~3 साल लगने की उम्मीद है
  • मौजूदा polyfill इस्तेमाल किया जा सकता है, लेकिन review प्रक्रिया के दौरान API बदल सकती है, इसलिए इसकी stability पर निर्भर न रहने की सलाह दी गई है

FAQ में संक्षेपित usage model

  • built-in Signals rendering technology से स्वतंत्र हैं
    • दस्तावेज़ बताता है कि Preact जैसे VDOM वाले, Solid जैसे native DOM वाले, और Vue जैसे mixed approach वाले रूप सभी संभव हैं
  • app developers के लिए आम तौर पर frameworks के जरिए Signals का उपयोग करना अधिक उपयुक्त है
    • framework Watcher, untrack, ownership, disposal, और DOM rendering scheduling को संभालता है
  • SSR, hydration, resumability के साथ इसका उपयोग किया जा सकता है
    • Qwik पहले से Signals को इन गुणों के साथ उपयोग करता है, और प्रस्ताव पक्ष मानता है कि Qwik के resumable Signals को State और Computed के संयोजन से model किया जा सकता है
  • Signals और Proxy एक-दूसरे के पूरक हैं
    • Proxy shallow object operations को intercept करता है, जबकि Signals data cell की dependency graph को coordinate करते हैं
    • Proxy के पीछे Signals रखने से nested reactive structure को अधिक ergonomic बनाया जा सकता है
  • Signals stream नहीं, बल्कि current value को दर्शाने वाले cells हैं
    • यदि State Signal पर लगातार दो बार write किया जाए और कोई काम न किया जाए, तो पहली write computed Signal या effect को दिखाई नहीं दे सकती
    • दस्तावेज़ इसे glitch-free execution के दूसरी तरफ की विशेषता मानता है, और कहता है कि streams के लिए async iterable या observable जैसी दूसरी संरचनाएं अधिक उपयुक्त हैं

1 टिप्पणियां

 
GN⁺ 2024-04-01
Hacker News की राय
  • क्या सिर्फ मुझे ही लगता है कि pure JavaScript example उल्टा पढ़ने और संभालने में ज़्यादा आसान है?
    कहा गया है कि “setup में noise और boilerplate ज़्यादा है”, लेकिन signals example भी उतना ही noisy और boilerplate से भरा लगता है, और शुरुआती लोगों के लिए समझने में मुश्किल एक नया concept भी जोड़ देता है
    “जब counter 2 से 4 में बदलता है तो parity नहीं बदलती, फिर भी गैरज़रूरी calculation और rendering होती है” — यह premature memoization जैसा लगता है
    अगर UI का कोई और हिस्सा counter update के साथ render होना चाहता है, तो वह strawman example सही नहीं है, यह बात ठीक है; उस समय signals, event handling, central state store (Redux जैसी चीज़ें) जैसे दूसरे तरीके इस्तेमाल किए जा सकते हैं
    अगर UI का कोई और हिस्सा सिर्फ isEven या parity पर निर्भर है, तो app की core structure होने पर approach बदली जा सकती है, लेकिन ज़्यादातर मामलों में ऐसा नहीं होता। “सिर्फ parity पर निर्भर render function को यह पता होना चाहिए कि उसे counter subscribe करना है” यह भी ज़रूरी नहीं कि अनुचित बोझ हो, और pure calculation function का फायदा है कि उसके inputs समझना आसान होता है

    • समझ नहीं आता कि इसे premature memoization क्यों माना जा रहा है। यह तो बस simple function में घटाया गया example है, और यह मानना मुश्किल है कि लोगों ने ऐसे use cases बिना असल ज़रूरत के गढ़ लिए होंगे
      UI development में increasingly इस्तेमाल हो रहे concept signals standardization की कोशिश सराहनीय है। boilerplate कितना है, event system खुद बनाना पड़ेगा या नहीं जैसे details पर बहस अलग रख दें, तो अगर कई frameworks signals इस्तेमाल कर रहे हैं, उसके पीछे कोई वजह हो सकती है, और भले समय लगे, standardization की कोशिश करने लायक है
    • सहमत। हालांकि Preact के signal docs देखें तो context कहीं ज़्यादा ठीक बैठता है
      https://preactjs.com/guide/v10/signals
      Preact में जब signal props या context के रूप में tree में नीचे जाता है, तो सिर्फ signal reference pass होता है, और component value नहीं बल्कि signal को देखता है, इसलिए signal update करने पर component को re-render न करना संभव हो सकता है। असल में .value access करने वाले tree के component तक सीधे जा सकते हैं
      साथ ही signal यह track करता है कि value कब access और कब update होती है, और Preact में component के अंदर signal की .value access करने पर जब उस signal की value बदलती है तो component अपने-आप फिर से render होता है
    • JavaScript में reactivity built-in नहीं है, इसलिए reactivity जोड़ेंगे तो abstraction cost आना ही है। यह ज़रूरत पड़ने पर इस्तेमाल करने के लिए है, state handle करने का default तरीका होना ज़रूरी नहीं
      अनुभव से बड़ा फायदा यह है कि reactive state को modularize किया जा सकता है। imperative style में बदलाव track करने के लिए extra state चाहिए होती है, और modularity abstraction से हासिल होती है। ज़रूरत हो तभी इस्तेमाल करें
      simple और लागू होने लायक example बनाना balance का मामला है। जहाँ reactivity का फायदा स्पष्ट होता है वे cases आम तौर पर ज़्यादा complex होते हैं, इसलिए उन्हें simple लेकिन कम लागू होने वाले example की तुलना में दिखाना मुश्किल होता है
    • इसे समझाने का तरीका बेहतर किया जा सकता है। छोटे example में समस्या साफ नहीं दिखती, बड़े scale पर सामने आती है। PR welcome
    • किसी खास complexity threshold के पार जाने पर design change की ज़रूरत पड़ना टालने लायक चीज़ है। pure JS approach की state graph complexity के लिहाज़ से scaling limit है, और असली समस्या threshold के पहले-बाद की usability नहीं, बल्कि उस threshold को पार करते ही usability का discontinuous तरीके से बदल जाना है
  • जब JavaScript में Promises जोड़े जा रहे थे, तो मुझे यह सोचकर झिझक थी कि क्या हर जगह new Promise लिखना पड़ेगा
    असल में मैंने सीधे new Promise जितनी बार लिखा है, उसे दो हाथों पर गिना जा सकता है। इसके बजाय, खासकर third-party libraries से काम करते समय .then कहीं ज़्यादा बार इस्तेमाल होता है
    आखिरकार Promise को JavaScript में जोड़ने का रोज़मर्रा का असर यह रहा कि third-party libraries द्वारा दी जाने वाली तरह-तरह की special behaviors और capabilities के लिए एक काफी simple, आम तौर पर robust और लगभग universal interface मिला। file पढ़ना हो, API request हो या build step output, .then(res => …) लिखते ही ऐसा लगता है कि किसी काम कर सकने वाली चीज़ तक आधे रास्ते पहुँच गए
    अगर यह Signal proposal reactive UI frameworks के Cambrian explosion के बीच वैसी ही भूमिका निभा सके, तो मैं इसके पक्ष में हूँ। इससे आगे, यह reactivity को UI के बाहर extend करने में भी मदद कर सकता है। UI state न होने वाली चीज़ों के लिए incremental recomputation state trees की कल्पना मैंने अक्सर की है

    • मेरे हिसाब से Promises मुख्य रूप से async/await जोड़ने के लिए आए थे, और असल quality of life में बड़ा सुधार उसी ने किया। practical work में सीधे new Promise लिखने की ज़रूरत कम ही पड़ती है
      शुरुआती Promise का .then nested delegate से बड़ा improvement था और simple chains के लिए ठीक है, लेकिन अगर conditional तरीके से अलग-अलग Promises chain करने हों, हर chain के लिए अलग error handling हो, या early return चाहिए हो, तो code पढ़ने और संभालने में काफी कठिन हो सकता है
      async/await इस्तेमाल करने पर calls ऐसे लिख सकते हैं जैसे वे Promise न हों, किसी खास Promise call के आसपास आसानी से try/catch लगा सकते हैं, और early return भी natural तरीके से हो जाता है
  • समझ नहीं आता कि यह भाषा का हिस्सा क्यों होना चाहिए। यह library से हो सकता है और ऐसी libraries पहले से मौजूद हैं। यह छोटा है, इसलिए code में शामिल करने पर भी बड़ा बोझ नहीं है, और भाषा में कुछ जोड़ना अपने आप में लक्ष्य नहीं होना चाहिए
    अभी की JS UI libraries ने signals को इतना बढ़िया design कर दिया है कि इसे भाषा का हिस्सा होना चाहिए—ऐसा सोचना घमंड है। signals की कई implementations हैं जिनमें अलग-अलग trade-offs हैं, और उनमें से कोई भी JavaScript specification में खास जगह पाने की हकदार नहीं है
    ये libraries signals इस्तेमाल करने से पहले virtual DOM इस्तेमाल करती थीं। सौभाग्य से virtual DOM JS का हिस्सा नहीं बना, तो signals में अलग क्या है? कुछ नहीं। standardize करने का तर्क virtual DOM के समय से भी कमजोर है
    क्या हम ऐसे runtime में, जहाँ web को तोड़े बिना अब न चाही जाने वाली features हटाने का लगभग कोई तरीका नहीं है, हर trend को जमा करते रहेंगे? यह काफी अल्पदृष्टि है

    • इसमें कुछ बात सही है। हम गलत चीज नहीं चाहते, लेकिन सही चीज चाहते हैं
      Reactive UI जीत चुका है। छोटी applications में भी state management करते समय complexity का फटना ही वह मुख्य वजह है जो pure JS इस्तेमाल करना मुश्किल बनाती है। मेरे लिए कोई भी reactive framework pure JS से बेहतर है, और अगर ऐसा है तो शायद कोई missing building block है
      अब करीब 10 साल हो चुके हैं, इसलिए standardize करने लायक सीमा पर विचार करने का समय है। Promise की तरह, अगर सही तरीके से किया जाए तो यह बहुत common use cases की complexity घटा सकता है
      बेहतर मूल्यांकन यह पूछना है: “क्या मौजूदा reactive frameworks इस proposal का इस्तेमाल करेंगे?” अगर नहीं, तो क्यों, क्या missing है, क्या unnecessary है, और दूसरी भाषाओं के UI व reactivity से क्या सीखा जा सकता है—यह देखना चाहिए। बिखरे हुए अनुभव को refine करने लायक है
    • Signals को standardize करने का एक अच्छा कारण यह है कि debugging किसी बुरे सपने जैसी लगती है। imagine करें कि computed signals का एक deep tree एक-दूसरे को chain में fire कर रहा है, और आपको उस chain reaction की शुरुआत ढूँढनी है। standardize होने पर developer tools उसके आसपास बनाए जा सकते हैं
    • standard library के ज्यादातर हिस्सों के बारे में यही कहा जा सकता है। हालांकि motivation में कहा गया है कि JS की अपेक्षाकृत छोटी standard library को expand करने की दिशा है, ताकि common tasks के लिए हर बार package import न करना पड़े
      इसकी जरूरत पर बहस हो सकती है, लेकिन अगर standard library को expand करना है तो popular चीजों को देखना अच्छा तरीका लगता है
      signals virtual DOM का replacement नहीं हैं
    • Observable proposal याद आता है
  • जब पूरी application में किसी चीज का signal देना हो तो events इस्तेमाल करते हैं
    window.dispatchEvent(new Event('counterChange'));
    और application का कोई भी हिस्सा जो react करना चाहता हो, इस तरह subscribe कर सकता है
    window.addEventListener('counterChange', () => { ... do something ... });
    इस तरीके में दिक्कत क्या है?

    • ऐतिहासिक रूप से यही example दिखाता है कि web jQuery में कैसे विकसित हुआ, और वहाँ से Angular और React की दुनिया में कैसे बँट गया
      Event handling बहुत आसानी से messy हो जाती है। और गहराई में देखना हो तो event bubbling और propagation देखें
      बड़ी applications को robust event handling चाहिए, और यही Angular, Vue जैसे frameworks का आजकल कम दिखाई देने वाला फायदा है
      frameworks के बिना standard event handling API को जस का तस इस्तेमाल करना आप नहीं चाहेंगे। बहुत सारे elements के लिए add, delete, clone, fire, remove, one-time fire आदि संभालते-संभालते गंभीर unwanted side effects हो सकते हैं
    • लेख के मुताबिक event publishers/observables कई बार call होने पर अनावश्यक काम कराते हैं
      signals से फर्क यह है कि result value तभी compute होती है जब final consumer value पढ़ता है। signal में असल write होने के समय और async render update schedule करने को अलग किया जाता है, और observer द्वारा की जाने वाली computation chain render के दौरान सिर्फ एक बार चलती है
      signal से भेजी गई intermediate values गायब हो जाती हैं, इसलिए उनके अंदर ज्यादा interesting काम करना मुश्किल है, और असल में यह rendering cycle को coordinate करने के लिए एक advanced abstraction layer के करीब है
    • Signals भी आखिरकार publish/subscribe ही हैं, लेकिन API इस्तेमाल में बेहतर है। क्योंकि listeners अपने आप add और remove हो जाते हैं
      performance भी बेहतर हो सकती है। उदाहरण के लिए अगर कोई computation दो values पर depend करती है, तो result = a ? b : 0 में अगर a false है, तो b बदलने पर भी recompute करने की जरूरत नहीं। signals में यह अपने आप हो जाता है, लेकिन traditional publish/subscribe में इसके लिए काफी code चाहिए
    • इस pattern को 10 साल से ज्यादा इस्तेमाल किया है। मुश्किल यह है कि समय के साथ कोई listener किसी दूसरे event को trigger कर सकता है, और फिर दूसरा event पहले routine में लौटकर कभी न खत्म होने वाला listener loop बना सकता है
      यह guarantee करना भी मुश्किल है कि सभी listeners ऐसी chain triggers नहीं बनाएंगे
    • उस तरीके में proposal द्वारा highlight की गई publish/subscribe architecture की सभी कमियां हैं
  • दशकों से यह समझने की कोशिश कर रहा हूँ कि लोगों को state tracking और DOM updates इतने कठिन क्यों लगते हैं
    बेशक थोड़ा अनुशासन चाहिए, लेकिन हर कुछ साल में आने वाले समाधानों की तुलना में यह कहीं ज़्यादा सरल लगता है। Backbone, Knockout, Angular, React, भाषा में ही बदलाव वगैरह—शायद मेरा सोचने का तरीका ही मूल रूप से अलग है
    फ़ंक्शन के नामों में भी यह दिखता है। innerText को update करने को “render” कहा जाता है, जबकि असल में वह render नहीं करता। ज़्यादा से ज़्यादा browser render करता है, और painting से जुड़े बाकी सभी कामों के लिए भी यही बात है। DOM के सबसे सरल functions में से एक को जटिल बनाने की हताश कोशिश जैसा लगता है, इसलिए सच में हैरानी होती है

    • सरल applications में यह आसान है
      ज़्यादा जटिल होने पर आसान नहीं रहता
    • “थोड़ा अनुशासन चाहिए” वाली बात से मुझे प्रबल एहसास होता है कि पुराने समय में आप ASM programmer के रूप में उन पागल portable C programmers पर नाराज़ होते, और बाद में C programmer के रूप में उन पागल memory-safe Java programmers पर नाराज़ होते
      programming की प्रगति को अच्छे नतीजे पाने के लिए ज़रूरी औपचारिक और कठोर अनुशासन को हटाने की प्रक्रिया के रूप में देखा जा सकता है
      इसका मतलब यह नहीं कि React अगला evolution है, लेकिन signals निश्चित रूप से सही दिशा में एक कदम हैं
    • दशकों का समय लंबा होता है। आपको याद होगा कि अलग-अलग browsers में DOM updates कितने जटिल हुआ करते थे
      DOM को data state के साथ sync करना अपने-आप में बहुत कठिन नहीं है, लेकिन इसे 60fps पर बहुत performant तरीके से करना बेहद कठिन है। खासकर तब, जब ऐसा API बनाना हो जो leak न करे और बहुत झंझट वाला भी न हो
      changes को live DOM tree में transform करके reflect करने के बजाय, ईमानदारी से कहें तो game की तरह canvas पर pixels draw करना शायद आसान हो सकता है
    • हम सैकड़ों हज़ार lines वाला FX trading application बना रहे हैं। यह एक heavy desktop app को replace करने के स्तर का है, और इसमें 20–30 developers तथा अपने-अपने developers वाले कई customers हैं। अगर कोई framework के बिना करने को कहे, तो बस शुभकामनाएँ ही दे सकता हूँ
    • पूरी तरह यही सोचता हूँ। मैंने बहुत complex और interactive SPAs भी develop की हैं, लेकिन अभी तक वह problem सामने नहीं आई जिसे ये चीज़ें solve करने का दावा करती हैं
  • Promises एक अच्छी success story हैं, लेकिन अगर async/await नहीं होता तो उन्हें standardize करना अनिवार्य नहीं था
    कहा जा रहा है कि current draft Angular, Bubble, Ember, FAST, MobX, Preact, Qwik, RxJS, Solid, Starbeam, Svelte, Vue, Wiz आदि के authors/maintainers के designs पर आधारित है; उत्सुकता है कि मौजूदा library authors इस proposal को कैसे देखते हैं। React का list में न होना भी रोचक है
    Signals channels से थोड़े मिलते-जुलते हैं, लेकिन फर्क यह है कि यह single receiver नहीं बल्कि broadcast है। अगर इसका उपयोग करके web workers onMessage callback के बजाय channels से communicate कर सकें, तो अच्छा होगा। खासकर अगर Go की तरह signals/channels/promises के ऊपर select किया जा सके, तो कई concurrent messaging mechanisms को callbacks से manage करने की तुलना में syntax का फायदा होगा। जैसे signals को Promise.any में शामिल करने योग्य बनाना

    • “async/await न होता तो standardize करने की ज़रूरत नहीं थी” से मैं कड़ा असहमत हूँ
      x instanceof Promise बस काम नहीं करता। अगर मेरी library का then method catch callback लेता है और दूसरी library का नहीं, तो वे चुपचाप interoperable नहीं होंगे और पता लगाने का कोई तरीका भी नहीं होगा। finally कब execute होता है? callbacks कितने asynchronous तरीके से execute होंगे, इसे लेकर क्या उम्मीद की जा सकती है?
      standard न हो तो Promise इस्तेमाल करने वाली हर library को अपना polyfill लाना पड़ेगा, क्योंकि पहले से मौजूद चीज़ पर भरोसा नहीं किया जा सकता। और दूसरी libraries के Promises को भी वास्तव में consume नहीं किया जा सकता, क्योंकि यह भरोसा नहीं किया जा सकता कि वे अपेक्षित तरीके से behave करेंगे
      यह अनुमान नहीं, बल्कि कई सालों तक सच में ऐसा ही था, और यह वह नरक था जिसे बहुत लोगों को झेलना पड़ा
    • async/await से अलग भी standardization के फायदे हैं। JavaScript engines Promise-heavy applications के लिए performance optimizations कर पाए, और standard न होता तो यह संभव नहीं होता
    • React इस list में इसलिए नहीं है क्योंकि signals, Preact के विपरीत, React core API का हिस्सा नहीं हैं
      धुंधली-सी intuition यह है कि signals generalized useEffect() से बहुत मिलते-जुलते हैं, इसलिए अगर वे React में आए तो render cycle के दौरान क्या हो रहा है, यह और confusing बना सकते हैं। अच्छा हो या बुरा, React ने signals से अलग update approach चुनी है। हालांकि applicability को लेकर मैं गलत भी हो सकता हूँ
    • React इस list में इसलिए नहीं है क्योंकि उसका effect imperative नहीं बल्कि declarative है। props changes और re-render को भी एक level abstracted declarative माना जा सकता है। useEffect imperative behavior को साफ़-साफ़ isolate करता है
      यह Ember data binding जैसा बहुत दिखता है, और अंततः imperative nightmare बन सकता है। default state “अपने ही पैर पर बंदूक चलाने” जैसा है, और ऐसा न हो इसके लिए भारी cognitive burden और meta patterns चाहिए
    • शायद इसे “EventEmitter” कहना चाहिए
      https://nodejs.org/en/learn/asynchronous-work/the-nodejs-eve...
  • लिंक किए गए README के उदाहरण को समझ नहीं पाया
    // A library or framework defines effects based on other Signal primitives
    declare function effect(cb: () => void): (() => void);
    कौन-सी लाइब्रेरी? कौन-सा framework? यहीं मैं भटक गया। effect क्या है?
    effect(() => element.innerText = parity.get());
    effect को कैसे पता चलता है कि parity बदलने पर हर बार इस lambda को कॉल करना है? क्या यह signal बदलने पर हर बार इस lambda को कॉल करता है? अगर ऐसा है, तो caching की बात क्यों हो रही है? शायद ऐसा नहीं होगा
    खैर, अगर मैंने लेखकों की बात सही समझी है, तो signal का विचार अपने-आप में ठीक लगता है। लेकिन ऐसी अलग-थलग architecture की बड़ी समस्या यह है कि application पर्याप्त जटिल हो जाए तो किसी खास event के होने की वजह trace करते-करते आप खो जाते हैं। आदर्श रूप से signals को stack trace ठीक करना चाहिए, ताकि callback कॉल होने पर उसमें शुरू में signal trigger करने वाले code का stack trace पहले से शामिल हो

    • ऐसी कई libraries हैं जो effect नाम का function export करती हैं, और signal updates के जवाब में मनचाहा code चलाने देती हैं। Preact docs में signals और effects की अच्छी introduction है: https://preactjs.com/guide/v10/signals#effectfn
      मेरी समझ के मुताबिक, ऐसा effect function पहले callback को एक बार चलाता है ताकि यह पता कर सके कि execution के दौरान किन signals को access किया गया, और फिर उस callback जिन signals पर निर्भर है वे update होने पर callback को दोबारा कॉल करता है। अगर signal access synchronous और single-threaded है, तो callback execution के दौरान signal access होने की बात भर से पता चल सकता है कि उस callback को उस signal को subscribe करना चाहिए
      यह getter से भी संभव है। effect function getter method में signal की किस property को access किया गया, इसे track करता है; मेरी जानकारी में Vue 2 पहले यही तरीका इस्तेमाल करता था। Proxy से object access को भी track किया जा सकता है। proposal के example में signal value access करने के लिए कॉल किया जाने वाला get method है, और इस method के execution से dependencies track की जा सकती हैं
      [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
      [2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
    • parity.get() call, effect() को pass किए गए function के लिए dependency register करता है। parity update होने पर वह function call होता है
      यह हर signal बदलने पर call नहीं करता, बल्कि सिर्फ तब call करता है जब जिस signal पर dependency है वह बदले
      इस मामले में parity, isEven पर निर्भर है, और isEven, counter पर निर्भर है। इसलिए counter update होने पर पूरी dependency chain invalidate होती है, parity invalidate होता है, और callback फिर से execute होता है
    • signals implementation, नाम चाहे जो हो, आम तौर पर dynamic dependency graph बनाता है, और node पढ़ते समय edge बनती है। ऐसे काल्पनिक effect जैसे tracking context में, read signal के state node और effect के computation node के बीच edge बनाता है, और असल में दूसरा पहले के बाद के writes को subscribe कर लेता है, ताकि यह तय हो सके कि computation कब फिर से चलाना है
    • effect कोई भी मनचाहा function है जिसे आप call करना चाहते हैं
      signals में dependency tracking mechanism जानता है कि कौन-सी values फिर से calculate करनी हैं, और इसके परिणामस्वरूप system यह भी जान जाता है कि कौन-से functions फिर से call करने हैं
    • लगता है effect implementation के लिए watcher की जरूरत होगी
  • इससे जुड़ा S.js है: https://github.com/adamhaile/s
    मुझे signals पसंद हैं। UI बनाते समय मैं उन्हें किसी भी दूसरे primitive से ज्यादा पसंद करता हूं, शायद cassowary constraint algorithm ही एक अपवाद हो सकता है। जिन भी languages में मैं मजे के लिए लिखता हूं, उनमें signals की नकल करने की कोशिश करता हूं
    लेकिन मुझे बिल्कुल नहीं लगता कि यह JavaScript language itself में आने वाली चीज है। काश भाषा को कुछ समय के लिए छोड़ दिया जाए। लोग पहले ही इसे follow करने में मुश्किल महसूस कर रहे हैं, और TC-39 पहले ही लोगों को भाषा से डराकर दूर कर रहा है

  • यह मेरे पसंदीदा JS effect system, MobX, जैसा बहुत लगता है
    MobX version ऐसा है
    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);

    • MobX सच में signals ही है। बस dependencies को getter से explicitly track करने के बजाय यह proxy objects के जरिए implicitly track करने वाला signals है
  • “जो framework मैं आजकल इस्तेमाल कर रहा हूं, उसे standard library में bake कर दें!” जैसा feel है
    यह girlfriend का नाम शरीर पर tattoo करवाने जैसा है

    • यह वैसा नहीं है
      यह उस component को standard library में डालना है, जिस पर ज्यादातर frameworks का उपयोग converge हो चुका है
      Promises भी widely used होने के बाद standard library में आए थे, और यह भी कुछ वैसा ही है