जावास्क्रिप्ट कॉल स्टैक ओवरफ्लो, सेटटाइमआउट और AJAX प्रदर्शन में गिरावट

समस्या


कुछ समय पहले, जब josi इंजन के क्लाइंट (जावास्क्रिप्ट) भाग पर काम कर रहा था, तब तक काफी सामान्य स्टैक ओवरफ्लो समस्या उत्पन्न हो गई थी:
बिना किसी सीमा के अधिकतम सीमा: अधिकतम कॉल स्टैक का आकार पार हो गया (Google क्रोम)
यह आलेख सेटटिमाउट या सेटइंटरवल का उपयोग किए बिना एक समाधान पर चर्चा करता है।


दिल


इस व्यवहार का कारण ज्ञात और समझने योग्य है, और एक रूप या किसी अन्य में यह हमेशा निम्नलिखित के कारण होता है। शास्त्रीय (प्रत्यक्ष) पुनरावर्तन लगातार कॉल की एक श्रृंखला उत्पन्न करता है, जो तदनुसार कॉल स्टैक को भरने की ओर जाता है, हालांकि, परीक्षण के समय क्रोम कॉल काफी छोटा है, यह 500 कॉल में है, सफारी में, अगर मैं गलत नहीं हूं, तो भी। किसी भी मामले में, यह सीमा मूल्य है, जिसका अर्थ है कि इसे पार किया जा सकता है और एक अपवाद प्राप्त होता है। स्वाभाविक रूप से, इस तरह के एक लंबे कोड निष्पादन सिद्धांत रूप में वांछनीय नहीं है, और इससे बचा जाना चाहिए। और फिर भी, व्यक्तिगत रूप से, मैं भाग्य पर भरोसा नहीं करना चाहता, इस तथ्य के बावजूद कि जिस स्थिति में मुझे उत्पादन पर समस्या का सामना करना पड़ा, वह उत्पन्न नहीं होना चाहिए, मैंने इस मुद्दे का अध्ययन करने में समय बिताया।

निर्णय


क्लासिक समाधान (मेरा मतलब है कि यह पेशकश करने वाले अधिकांश लेख) अप्रत्यक्ष पुनरावृत्ति का उपयोग करना है: setTimeout या setInterval

एक उदाहरण के रूप में, मैं आपको एक सरल पुनरावर्ती कार्य दूंगा जिसका एकमात्र उद्देश्य इस सीमा से अधिक के बारे में छूट के साथ ही या बाद में स्टैक आकार सीमा को वापस करना है ...

function f(args) { var self=this; var k=args.k; //   try { f({k:k+1}); } catch(ex) { alert(k); } } 


एक ही बेकार फ़ंक्शन, लेकिन अब सैद्धांतिक रूप से अनंत है, जब तक कि कश्मीर ओवरफ्लो न हो

 function f(args) { var self=this; var k=args.k; //   ,   setTimeout setTimeout(function(){ f({k:k+1}) }, 0); } 

वर्तमान फ़ंक्शन पुनरावृत्ति के लिए setTimout का उपयोग करके तुरंत समाप्त होता है, और अगला कॉल ईवेंट आधार पर किया जाता है।
इस दृष्टिकोण का नकारात्मक पक्ष इसका अत्यंत कम प्रदर्शन है, इस तथ्य के बावजूद कि हम शून्य विलंब का संकेत देते हैं। फ़ंक्शन को ब्राउज़र के आधार पर कहा जाएगा, औसतन, 10 एमएस के बाद पहले नहीं। लेकिन हम कॉल स्टैक से अधिक के साथ संघर्ष कर रहे हैं, जिसका अर्थ है कि हमारे फ़ंक्शन को सैकड़ों बार कहा जाता है, जिसका अर्थ है कि प्रत्येक 100 कॉल के लिए ~ 1 s का प्रदर्शन नुकसान। विस्तृत परीक्षण यहां पाया गया
मेरे लिए सबसे आसान काम प्रत्यक्ष और अप्रत्यक्ष कॉल का उपयोग करके वैकल्पिक रूप से सहजीवन को व्यवस्थित करना था, ताकि जब एक निश्चित काउंटर वैल्यू हो, तो एक अप्रत्यक्ष कॉल के साथ स्टैक को बाधित करें। भाग में, इस तरह के समाधान का अब उपयोग किया जा रहा है। लेकिन यहां भी, सब कुछ इतना सरल नहीं है, खासकर अगर पुनरावृत्ति को कई कार्यों के एक लूप द्वारा दर्शाया जाता है।
इस तरह के समाधान के सार को दर्शाते हुए एक सरल उदाहरण है:

 var max_call_i=300; function f(args) { var self=this; var k=args.k; var call_i=args.call_i //alert(k); if (call_i>=max_call_i) { //   ,   setTimeout setTimeout(function(){ f({k:k+1, call_i:call_i+1}) }, 0); } else { //    f({k:k+1, call_i:call_i+1}); } } 


मेरे कोड में, टेम्पलेट इंजन में एक समस्या उत्पन्न हुई, जो कुछ समय पहले ही नए प्रतिमान के अनुसार फिर से लिखी गई थी। मैं दत्तक वास्तुकला को नहीं छोड़ना चाहता था। इसी समय, उत्पादकता में वास्तविक गिरावट 20-30% थी - जो कि केवल राक्षसी थी। ऊपर प्रस्तावित समाधान भी आदर्श नहीं है: 5-7% की उत्पादकता में गिरावट। यह मुझे शोभा नहीं देता: मैंने बहुत कुछ जाना, और जो मुझे चाहिए उस पर हमला किया।
और यह उसी से एक परीक्षण है, जिसमें से यह देखा जा सकता है कि प्रस्तावित दृष्टिकोण, सेटटाइमआउट 0 की तुलना में, बहुत अधिक उत्पादक है, जो व्यवहार में मेरे मामले में प्रदर्शन में 3% से अधिक गिरावट नहीं देता है ...

यह समाधान window.postMessage और element.addEventListener के संयोजन पर आधारित है, क्रॉस-ब्राउज़र मेरे लिए पर्याप्त है (यानी +)।

मैंने उपरोक्त लेख से एएमडी मॉड्यूल में फ़ंक्शन को फिर से काम किया। शायद यह किसी के लिए उपयोगी होगा ...

 define([], function () { return { args: // { indirect_call: { f_arr:[], msg_name:"indirect_call-message", handler_f:null, } }, /***    ***/ f_indirect_call:function(f) { var self=this; //           window if (t_uti.f_is_empty(self.args.indirect_call.handler_f)) { //   self.args.indirect_call.handler_f=function(event) { if (event.source == window && event.data == self.args.indirect_call.msg_name) { event.stopPropagation(); if (self.args.indirect_call.f_arr.length> 0) { var f = self.args.indirect_call.f_arr.shift(); f(); } } } window.addEventListener("message", self.args.indirect_call.handler_f, true); } self.args.indirect_call.f_arr.push(f); window.postMessage(self.args.indirect_call.msg_name, "*"); }, }; }); 

Source: https://habr.com/ru/post/In167033/


All Articles