15 पॉइंट द्वारा GN⁺ 2025-06-02 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • प्रोग्रेसिव JPEG की तरह, JSON डेटा को भी पहले अधूरी अवस्था में भेजने का तरीका पेश किया गया है ताकि क्लाइंट धीरे-धीरे पूरे कंटेंट का उपयोग कर सके
  • मौजूदा JSON parsing पद्धति में पूरी डेटा प्राप्त होने तक कोई काम शुरू नहीं किया जा सकता, जिससे अक्षमता की समस्या होती है
  • Breadth-first पद्धति में डेटा को कई chunks (हिस्सों) में बाँटा जाता है, और जो हिस्से अभी तैयार नहीं हैं उन्हें Promise के रूप में दिखाकर तैयार होते ही क्रमशः भरा जाता है, ताकि क्लाइंट अधूरे डेटा का भी उपयोग कर सके
  • यह अवधारणा React Server Components(RSC) की मुख्य innovation है, और <Suspense> के जरिए इच्छित चरणबद्ध loading state को नियंत्रित किया जाता है
  • डेटा streaming और जानबूझकर डिजाइन किए गए UI loading flow को अलग करके अधिक लचीला user experience दिया जा सकता है

प्रोग्रेसिव JPEG और प्रोग्रेसिव JSON का विचार

  • प्रोग्रेसिव JPEG में इमेज को ऊपर से नीचे एक बार में लोड करने के बजाय, पहले पूरी इमेज धुंधली अवस्था में दिखाई जाती है और फिर वह धीरे-धीरे साफ होती जाती है
  • इसी तरह, JSON ट्रांसमिशन में भी प्रोग्रेसिव तरीका लागू करने पर पूरी चीज़ तैयार होने तक इंतज़ार किए बिना कुछ डेटा का तुरंत उपयोग किया जा सकता है
  • उदाहरण JSON डेटा संरचना में, सामान्य तरीके से parsing तभी संभव है जब आख़िरी byte तक सब कुछ प्राप्त हो जाए
  • इसके कारण क्लाइंट को सर्वर के धीमे हिस्सों (जैसे धीमे DB से comments लाना) सहित सब कुछ ट्रांसमिट होने तक इंतज़ार करना पड़ता है, और यही आज के मानक की बहुत बड़ी अक्षमता है

Streaming JSON parser की सीमाएँ

  • Streaming JSON parser अपनाने पर अधूरी (मध्यवर्ती) data object tree बनाई जा सकती है
  • लेकिन जब हर object के fields (जैसे footer, कई comment सूचियाँ आदि) का केवल कुछ हिस्सा ही आया हो, तब type mismatch होता है और यह समझना कठिन होता है कि क्या पूरा हो चुका है, जिससे उपयोगिता घट जाती है
  • HTML की streaming rendering की तरह, stream को क्रम से प्रोसेस करने पर एक धीमा हिस्सा पूरे परिणाम को देर से पहुँचाता है
  • यही वजह है कि सामान्यतः streaming JSON का उपयोग कम होता है

Progressive JSON संरचना का प्रस्ताव

  • पारंपरिक depth-first streaming (यानी tree structure के भीतर नीचे तक घूमते हुए भेजने का तरीका) के बजाय, Breadth-first (चौड़ाई-प्रथम) तरीका अपनाया गया है
  • पहले सिर्फ top-level object भेजा जाता है, और नीचे के values को Promise जैसे placeholders के रूप में छोड़कर तैयार होते ही अलग-अलग chunks में भरा जाता है
  • उदाहरण के लिए, सर्वर जब भी async data loading पूरी करता है, उससे संबंधित chunk भेज देता है, और क्लाइंट उतने तैयार हिस्से का तुरंत उपयोग कर सकता है
  • इससे async data reception (early load) संभव हो जाता है, और कई धीमे हिस्सों के पूरा होने तक पूरी तरह इंतज़ार नहीं करना पड़ता
  • अगर क्लाइंट को chunk-आधारित non-sequential और partially sequential reception के लिए मज़बूती से बनाया जाए, तो सर्वर विभिन्न chunk विभाजन रणनीतियों को लचीले ढंग से लागू कर सकता है

Inlining और Outlining: कुशल डेटा ट्रांसमिशन

  • प्रोग्रेसिव JSON streaming format में reusable objects (जैसे एक ही userInfo को कई जगह refer करना) को duplicate किए बिना अलग chunk के रूप में निकालकर हर जगह उसी reference से इस्तेमाल किया जा सकता है
  • केवल धीमे हिस्सों को अलग करके placeholder के रूप में भेजा जाता है, और बाकी को तुरंत भरकर कुशल data stream बनाई जाती है
  • जब एक ही object कई बार आता है, तो उसे केवल एक बार भेजकर दोबारा इस्तेमाल (Outlining) किया जा सकता है
  • इस तरीके से circular references (जब object खुद को refer करे) भी सामान्य JSON की तरह कठिन नहीं रहते, और chunks के बीच indirect references से स्वाभाविक रूप से serialize किए जा सकते हैं

React Server Components(RSC) में प्रोग्रेसिव streaming का कार्यान्वयन

  • वास्तविक React Server Components, प्रोग्रेसिव JSON streaming मॉडल लागू करने का एक प्रमुख उदाहरण हैं
    • सर्वर बाहरी डेटा (जैसे Post, Comments) को async तरीके से लोड करता है
    • क्लाइंट में जो हिस्से अभी नहीं पहुँचे हैं उन्हें Promise की तरह रखा जाता है, और वे तैयार होने के क्रम में UI को प्रोग्रेसिव तरीके से render किया जाता है
  • React के <Suspense> से जानबूझकर loading states सेट की जाती हैं
    • user experience में अनावश्यक screen jump से बचने के लिए Promise state (खाली जगह) को तुरंत दिखाने के बजाय <Suspense> fallback से चरणबद्ध loading दिखाया जा सकता है
    • भले ही डेटा जल्दी आ जाए, असली UI को डिजाइन किए गए चरणों के अनुसार प्रोग्रेसिव तरीके से दिखाने पर डेवलपर का नियंत्रण रहता है

सारांश और निहितार्थ

  • React Server Components की मुख्य innovation यह है कि component tree के props को बाहरी स्तर से भीतर की ओर प्रोग्रेसिव तरीके से stream किया जाता है
  • इसलिए सर्वर के पूरी तरह सभी डेटा तैयार करने तक इंतज़ार किए बिना, महत्वपूर्ण हिस्सों को पहले दिखाया जा सकता है और loading wait state को भी बारीकी से नियंत्रित किया जा सकता है
  • केवल streaming ही नहीं, बल्कि इसका उपयोग करने वाला programming model (जैसे React का <Suspense>) जैसा संरचनात्मक समर्थन भी ज़रूरी है
  • इससे धीमे डेटा के एक हिस्से के कारण पूरे परिणाम के रुक जाने जैसी मौजूदा ट्रांसमिशन bottleneck समस्याओं को कम किया जा सकता है

1 टिप्पणियां

 
GN⁺ 2025-06-02
Hacker News की राय
  • ऐसा लगता है कि कुछ लोग इस लेख को बहुत शाब्दिक रूप से ले रहे हैं, जबकि Dan Abramov कोई नया फ़ॉर्मैट Progressive JSON प्रस्तावित नहीं कर रहे हैं
    • यह लेख React Server Components के विचार और component tree को JavaScript object के रूप में दर्शाकर उसे stream के रूप में भेजने की प्रक्रिया समझाता है
    • यह तरीका React component tree में ‘holes’ छोड़ने देता है, ताकि शुरुआत में loading state या skeleton UI दिखाया जा सके, और सर्वर से वास्तविक data मिलने पर उस हिस्से को पूरी तरह render किया जा सके
    • इससे और अधिक सूक्ष्म स्तर पर loading indicator और तेज़ first paint संभव होता है
  • मेरे हिसाब से, अगर लोग इस विचार को आगे बढ़ाकर दूसरे तरीकों में लागू करना चाहें तो यह ठीक है
    • इरादा यह था कि RSC के data serialization के तरीके को सिर्फ React तक सीमित न रखकर, एक अधिक सामान्य pattern के रूप में समझाया जाए
    • उम्मीद है कि React Server Components में मिले कई विचार दूसरी तकनीकों और ecosystems में भी समा जाएँ
  • मुझे progressive loading का तरीका, खासकर वह अनुभव जिसमें content लगातार हिलता-डुलता या jump करता रहता है, ज़्यादा पसंद नहीं है
    • loading के दौरान खाली state UI दिखाने वाला pattern विशेष रूप से खटकता है
  • कुछ समय पहले तक जब मैं Ember इस्तेमाल करता था, तब भी ऐसा ही तरीका था, और Ajax endpoint लिखना बहुत कष्टदायक था
    • शायद इरादा tree structure को rearrange करके कुछ child elements को file के अंत में रखना था, ताकि DAG (acyclic graph) handling अधिक कुशल हो सके
    • SAX style streaming parser का उपयोग करने पर data आंशिक रूप से आते ही painting शुरू की जा सकती है
    • लेकिन single-threaded VM में अगर काम का क्रम ठीक से डिज़ाइन न किया जाए, तो समस्या और बढ़ सकती है
  • मैं पहले से ही AI tools के साथ integration में streaming partial JSON (Progressive JSON) का वास्तविक उपयोग कर रहा हूँ
    • मेरा अनुभव है कि यह तरीका सिर्फ RSC तक सीमित नहीं है; कई जगह काम आता है और व्यवहारिक रूप से client और server दोनों के लिए मूल्यवान है
  • मैंने Dan की "2 computers" प्रस्तुति और हाल की RSC पोस्ट्स सब देखी हैं
    • Dan React ecosystem के सबसे अच्छे व्याख्याकारों में से हैं, लेकिन अगर किसी तकनीक को इतना कठिन समझाना पड़ रहा है, तो
      1. या तो वह सच में ज़रूरी नहीं है
      2. या abstraction में समस्या है
    • अधिकांश frontend developers अब भी RSC की अवधारणा को पूरी तरह नहीं समझते
    • Vercel ने Next.js को default React framework जैसा बना दिया है, और RSC adoption भी उसी लहर के साथ फैल रहा है
    • Next.js इस्तेमाल करने वाले लोग भी अक्सर Server Component boundaries को साफ़-साफ़ नहीं समझते, और एक तरह का ‘cargo cult’ adoption बहुत दिखता है
    • React ने Vite से जुड़ा PR स्वीकार नहीं किया, यह भी संदिग्ध लगता है। ऐसा भी लग सकता है कि RSC push असल में users या developers के लिए नहीं, बल्कि platform कंपनियों की hosting platform बेचने की रणनीति है
    • पीछे मुड़कर देखें तो Vercel ने React की मूल टीम के कई लोगों को hire किया; यह भी React के भविष्य को दिशा देने की मंशा जैसा लगता है
    • किसी ने ऐतिहासिक प्रेरणा या पृष्ठभूमि के बारे में इस आकलन को ग़लत बताया और Vite support की मौजूदा स्थिति भी समझाई
    • कहा गया कि Vite integration में DEV environment में bundling की तकनीकी बाधा है, और Vite टीम अभी इस पर सुधार कर रही है
    • यह कहना कि लोग RSC नहीं समझते, अपने-आप में एक circular argument है
    • RSC नापसंद हो सकता है, लेकिन उसके भीतर दूसरी तकनीकों में अपनाने लायक काफ़ी दिलचस्प विचार हैं
    • उद्देश्य मनवाना नहीं, बल्कि इतना है कि लोग जो अजीब लेकिन उपयोगी लगे, उसे अपने तरीके से ले जाएँ
  • बेशक अब भी SPA को static site बनाकर CDN पर डाला जा सकता है, और Next.js को “dynamic” mode में self-host भी किया जा सकता है
    • लेकिन Next.js की पूरी serverless rendering क्षमताओं को Vercel के अलावा कहीं और पूरी तरह लागू करना कठिन है (undocumented “magic” की वजह से)
    • इस समस्या के लिए भी multi-platform पर consistent API देने हेतु adapter लाने का आधिकारिक प्रस्ताव रखा गया है: https://github.com/vercel/next.js/discussions/77740
    • मेरा मानना है कि RSC push सिर्फ corporate interest की वजह से नहीं, बल्कि इस समझ से निकला है कि पारंपरिक website build pattern (SSR + client पर थोड़ा progressive enhancement) में वास्तव में कई फ़ायदे हैं
    • सिर्फ SSR में भी यह समस्या रहती है कि बहुत सारा business logic अनावश्यक रूप से client की तरफ़ चला जाता है
  • RSC अपने-आप में दिलचस्प तकनीक है, लेकिन production में उतनी व्यावहारिक नहीं लगती
    • complex components render करने के लिए बड़े पैमाने पर Node/Bun backend server बनाए रखना बोझिल है
    • इसके बजाय static pages या React SPA + Go API server का संयोजन कहीं अधिक कुशल है
    • काफ़ी मिलते-जुलते परिणाम बहुत कम resources में हासिल किए जा सकते हैं
  • किसी नई तकनीक को समझाना जटिल हो, तो इससे यह ज़रूरी नहीं कि वह बेकार है या abstraction ग़लत है; कुछ समस्याएँ ऐसी होती हैं जिनके लिए यह जटिलता स्वीकार करने लायक होती है
    • देखना होगा कि आगे यह तकनीक कैसे विकसित होती है
  • मुझे लगता है कि RSC की code structure का उपयोग करके HTML/CSS/JS को छोटे टुकड़ों में बाँटकर static page build भी किया जा सकता है
    • लेख में सुझाए गए ‘$1’ placeholder को URI से भी बदला जा सकता है, तब सर्वर ज़रूरी नहीं होगा (ज़्यादातर मामलों में dynamic SSR अनिवार्य नहीं है)
    • कमी यह है कि content बदलने पर update pipeline की गति, खासकर S3 पर compiled static site की streaming deployment में, महत्वपूर्ण हो जाती है
    • जैसे कई prerendered articles वाली किसी news site में, अगर content का सिर्फ कुछ हिस्सा बदले, तो सिर्फ वही हिस्सा कुशलतापूर्वक rebuild करने के लिए smart content diff handling चाहिए
  • व्यवहार में अक्सर यह एहसास होता है कि performance optimization के नाम पर frontend में milliseconds बचाने के लिए कई MB data लाया जाता है और जटिल logic चलाया जाता है, जबकि वास्तव में BFF या architecture सुधारना और अधिक lean API बनाना ज़्यादा उत्पादक समाधान होता है
    • GraphQL, http2 आदि के साथ कई कोशिशें हुईं, लेकिन वे मूल समस्या का समाधान नहीं थीं; web standards के विकास के बिना paradigm shift मुश्किल है
    • नए frameworks भी इसी सीमा में बंधे हैं
  • RSC मूल रूप से, जैसा लेख के अंत में समझाया गया है, BFF (Backend for Frontend) की भूमिका निभाता है
    • यह API logic को componentize करने की संरचना है
    • मेरा लंबा लेख देखें: https://overreacted.io/jsx-over-the-wire/ (BFF का ज़िक्र पहले section के बीच में है)
  • “page loading ms कम होना” किस अर्थ में कहा जा रहा है, इस पर निर्भर करता है
    • अगर Time to first render और time to visually complete optimize करना है, तो पहले खाली skeleton UI भेजना और फिर API से data लेकर hydrate करना अनुभव के हिसाब से सबसे तेज़ लगता है
    • इसके उलट, अगर time to first input और time to interactive तेज़ करना है, तो user data को तुरंत render कर पाना ज़रूरी है, और इस मामले में backend कहीं अधिक फायदेमंद है (कम network calls)
    • ज़्यादातर मामलों में users यही ज़्यादा पसंद करते हैं; CRUD SaaS apps के लिए server-side rendering, जबकि Figma जैसे design-केंद्रित apps के लिए client पर static data + अतिरिक्त data fetch ज़्यादा उपयुक्त है
    • “हर समस्या के लिए एक ही समाधान” जैसी कोई चीज़ नहीं है; optimization का बिंदु एक व्यक्तिपरक चुनाव है
    • developer experience, team structure जैसे कई तत्व तकनीकी चयन को प्रभावित करते हैं
  • अब समझ आया कि Facebook लोड करते समय मुख्य content हमेशा सबसे अंत में क्यों render होता है
  • यहाँ BFF से क्या मतलब है, यह पूछने वाला सवाल भी आया
  • abbreviations बहुत ज़्यादा हैं, इसलिए FE और BFF क्या हैं, यह पूछने वाली प्रतिक्रिया भी दिखी
  • मैं खुद Progressive JSON का विचार सीधे इस्तेमाल नहीं करना चाहूँगा; मुझे लगता है कि कई alternatives मौजूद हैं
    • सबसे सरल समाधान है एक विशाल JSON object को कई हिस्सों में बाँटना, यानी ‘JSON lines’ के रूप में भेजना
    • header information एक बार भेजें, और विशाल array को line-by-line भेजें ताकि stream processing अधिक कुशल हो
    • अगर object और बड़ा हो, तो इस तरीके को recursively भी लागू किया जा सकता है, लेकिन यह बहुत जटिल हो सकता है
    • server अगर properties के order की स्पष्ट गारंटी दे, तो progressive parsing और separation भी संभव है
    • बहुत बड़े structures के लिए यह शायद पर्याप्त न हो, लेकिन बड़े JSON के आम उपयोग मामलों में यह काफ़ी व्यावहारिक tool है
  • ‘holes’ को explicitly चिह्नित किए बिना भी streaming messages को क्रमवार भेजकर सिर्फ delta (diff) भेजने का तरीका संभव है
    • ‘Mendoza’ delta format के साथ Go और JS/Typescript में बहुत compact तरीके से patch (diffs) भेजे जा सकते हैं: https://github.com/sanity-io/mendoza, https://github.com/sanity-io/mendoza-js
    • zstd के binary diff तरीके या Mendoza की तरह, serialized data का सिर्फ कुछ हिस्सा memory में रखकर efficient patching की जा सकती है
    • React को भी diff compare करने या सिर्फ changes inject करने का तरीका चाहिए, इसलिए यह एक सार्थक approach है
  • UI data streaming में सिर्फ खाली array या null काफ़ी नहीं होते; कौन-सा data अभी pending है, इसकी अलग जानकारी भी चाहिए
    • GraphQL streaming payload valid data schema, pending information, और बाद की patch handling का मिश्रित तरीका अपनाता है
  • कौन-सा हिस्सा ‘hole’ है, यह पता हो तो loading state दिखाना आसान होता है
  • client पर JSON को progressively decode करने के लिए jsonriver नाम की library का परिचय दिया गया: https://github.com/rictic/jsonriver
    • API बहुत सरल है, performance अच्छी है और testing भी पर्याप्त है
    • यह streamed string chunks को धीरे-धीरे अधिक पूर्ण values में parse करती है
    • अंतिम परिणाम JSON.parse के समान होने की गारंटी देती है
  • अगर data tree structure में है, तो यह किसी भी structure पर लागू किया जा सकने वाला दिलचस्प तरीका हो सकता है
    • tree data को parent, type, data vectors और string table के रूप में व्यक्त किया जाए, तो बाकी सब कुछ कुछ integers में समेटा जा सकता है
    • string table और type info को header के रूप में upfront भेजकर, parent और data vector chunks को node units में stream किया जा सकता है
    • depth-first या breadth-first streaming के लिए सिर्फ chunk order बदलना काफ़ी है
    • इससे network पर चलने वाले apps में load-time UX काफ़ी सुधर सकता है
    • tables और node chunks को बारी-बारी भेजकर tree को किसी भी क्रम में web पर visualize किया जा सकता है
    • preorder traversal और depth information भर से node id के बिना भी tree structure पुनर्निर्मित किया जा सकता है
    • इस विचार पर एक छोटी library बनाना भी सार्थक प्रयास हो सकता है
  • तर्क यह भी है कि अधिकांश apps को ऐसे ‘परिष्कृत’ loading system की ज़रूरत ही नहीं होती, और ज़्यादातर मामलों में कई बार API calls करना ही पर्याप्त होता है
    • जवाब में कहा गया कि उद्देश्य सिर्फ RSC wire protocol कैसे काम करता है, यह समझाना था; इसे स्वयं लागू करने की सलाह नहीं दी जा रही थी
    • अलग-अलग tools के सिद्धांत समझने से अलग जगहों पर ideas उधार लेने या remix करने में मदद मिलती है
    • कई बार call करने की रणनीति में n*n+1 समस्या हो सकती है, लेकिन OOP/ORM style में nested object भेजने के बजाय comments वाले उदाहरण की तरह flat रूप में भी भेजा जा सकता है
    • ऐसे में protobuf जैसे स्पष्ट types वाले endpoints बनाना भी लाभदायक हो सकता है
    • comments को अलग कर दें, तो 2 calls ही काफ़ी हैं (page+post अलग, comments अलग), और इससे pre-render optimization भी संभव है
    • यदि अच्छे prebuilt tools मौजूद हों, तो options की वास्तविक implementation complexity को अनावश्यक रूप से बढ़ाकर deep customization करने की ज़रूरत नहीं होती
    • यह समझना ज़रूरी है कि अत्यधिक जटिल features अंततः users या developers दोनों के लिए हानिकारक हो सकते हैं
    • जैसे 640K पर्याप्त है वाली बात, उसी तरह यह भी माना गया कि progressive/partial reads WASM युग के UX speed में वास्तव में बड़ा योगदान दे सकती हैं
    • protobuf जैसी binary encoding में partial read और well-defined streaming जुड़ जाए, तो engineering burden बढ़ेगा, लेकिन UX में बड़ा सुधार भी संभव है
  • Progressive JPEG मीडिया files के स्वभाव के कारण ज़रूरी है, लेकिन Text/HTML में इसकी ज़रूरत नहीं; बड़े JS bundles के कारण जटिलता बढ़ाना एक तरह का आत्मविरोधाभास है
    • यह भी बताया गया कि वास्तविक धीमापन सिर्फ data के ‘size’ की वजह से नहीं होता
    • server-side data query में समय लग सकता है, या network धीमा हो सकता है; ऐसे में progressive reveal का महत्व होता है
    • पूरा data तैयार होने तक इंतज़ार करने के बजाय, सही समय पर loading UI दिखाकर चरणबद्ध rendering करना वास्तविक user experience में लाभ देता है
  • endpoint separation की रणनीति पहले से ही कई फ़ायदे देती है (Head of line blocking से बचाव, बेहतर filter options, live updates, स्वतंत्र performance improvements आदि)
    • यह दृष्टिकोण भी है कि application को document platform की तरह बरतने की कोशिश ही मूल समस्या है
    • वास्तविक applications ‘document’ की तरह काम नहीं करते, और इस अंतर को पाटने के लिए बहुत सारा अतिरिक्त code और infrastructure चाहिए होता है
    • अलग endpoints अपनाने के वास्तविक नुकसान और आगे की दिशा पर दो लंबे लेखों में विस्तार से चर्चा की गई है: https://overreacted.io/one-roundtrip-per-navigation/, https://overreacted.io/jsx-over-the-wire/
    • संक्षेप में, endpoints अंततः server/client के बीच ‘official’ API contract बन जाते हैं, और जैसे-जैसे code modular होता जाता है, performance की हानि (जैसे waterfall effect) हो सकती है
    • server पर निर्णयों को एक साथ process करना (coalescing) performance और architectural flexibility, दोनों में बेहतर विकल्प हो सकता है