प्रतिनिधि इंटरव्यू विषय

फ्रंटएंड इंटरव्यू: आप Debounce और Throttle कैसे लागू करते हैं?

फ्रंटएंडमध्यम
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

debounce(fn, wait, options) और throttle(fn, wait, options) लागू करें। Leading और trailing इनवोकेशन का समर्थन करें, नवीनतम arguments और this मान को सुरक्षित रखें, और cancel() व flush() को एक्सपोज़ करें। बताएं कि निरंतर इनपुट, maxWait, टाइमर डिले, कंपोनेंट क्लीनअप और डिटरमिनिस्टिक टेस्टिंग शुद्धता को कैसे प्रभावित करते हैं।

समस्या और दायरा

ब्राउज़र-फेसिंग कोड के लिए debounce(fn, wait, options) और throttle(fn, wait, options) लागू करें। लौटाए गए फ़ंक्शन को नवीनतम arguments और this को सुरक्षित रखना चाहिए, नवीनतम इनवोकेशन परिणाम लौटाना चाहिए, और cancel()flush() को एक्सपोज़ करना चाहिए। इसके ऑप्शंस leading, trailing, और debounce के लिए maxWait हैं।

डिफ़ॉल्ट debounce केवल trailing एज पर इनवोक होता है। एक leading कॉल बर्स्ट की शुरुआत में चलती है। यदि leading और trailing दोनों सक्षम हैं, तो एक अकेली कॉल leading एज पर एक बार चलती है; wait अवधि के भीतर दूसरी कॉल नवीनतम arguments के साथ एक trailing इनवोकेशन बनाती है। maxWait निरंतर इवेंट स्ट्रीम को काम को हमेशा के लिए टालने से रोकता है। Throttle प्रति wait-आकार के अंतराल में अधिकतम एक इनवोकेशन की अनुमति देता है और leading तथा trailing व्यवहार का समर्थन करता है।

यह एक frontend प्रश्न है क्योंकि इसका कॉन्ट्रैक्ट ब्राउज़र इनपुट, स्क्रॉल, रिसाइज़, रेंडरिंग और कंपोनेंट लाइफसाइकल द्वारा संचालित होता है। यही यूटिलिटी Node.js में भी चल सकती है, लेकिन रनटाइम बदलने से इंटरव्यू का मुख्य कौशल नहीं बदलता: UI टाइमिंग सिमेंटिक्स को एक छोटी स्टेट मशीन में बदलना। यह कार्यान्वयन JavaScript और O(1) ऑक्जिलरी स्टेट का उपयोग करता है।

इंटरव्यूअर क्या मूल्यांकन करते हैं

पहला संकेत यह है कि क्या उम्मीदवार टाइमर लिखने से पहले कॉन्ट्रैक्ट को परिभाषित करता है। “Debounce इंतज़ार करता है; throttle सीमित करता है” यह तय नहीं करता कि leading व्यवहार क्या होगा, क्या एक अकेली leading कॉल trailing भी करेगी, कौन से arguments जीतेंगे, या क्लीनअप का क्या अर्थ है। एक मजबूत उत्तर कॉल की टाइमलाइन लिखता है और उन विकल्पों को स्पष्ट करता है।

दूसरा संकेत स्टेट रीजनिंग है। maxWait, क्लॉक मूवमेंट, रिटर्न वैल्यूज़ और flush() के आने के बाद एक टाइमर वेरिएबल पर्याप्त नहीं होता। कार्यान्वयन को अंतिम कॉल समय, अंतिम वास्तविक इनवोकेशन समय, पेंडिंग arguments और रिसीवर, टाइमर हैंडल और नवीनतम परिणाम की आवश्यकता होती है। प्रत्येक भाग को एक कॉन्ट्रैक्ट नियम के अनुरूप होना चाहिए।

तीसरा संकेत ब्राउज़र जजमेंट है। टाइमर न्यूनतम डिले प्रदान करते हैं, सटीक डेडलाइन नहीं; लंबे टास्क्स और बैकग्राउंड थ्रॉटलिंग कॉलबैक को देर से चला सकते हैं। requestAnimationFrame() के साथ स्क्रॉल कार्य को थ्रॉटल करना कार्य को पेंट के साथ संरेखित करता है लेकिन यह स्वयं इवेंट दर को कम नहीं करता है। कभी-कभी scrollend, IntersectionObserver, या एक फ्रेमवर्क क्लीनअप हुक एक सामान्य टाइमर यूटिलिटी की आवश्यकता को समाप्त कर देता है।

अंत में, इंटरव्यूअर विज़ुअल अंतर्ज्ञान के बजाय टेस्ट्स की जांच करता है। वॉल-क्लॉक स्लीप्स टेस्ट को धीमा और अस्थिर (flaky) बनाती हैं। एक मजबूत उम्मीदवार समय और टाइमर को इंजेक्ट या रिप्लेस करता है, एक फेक क्लॉक को आगे बढ़ाता है, और सीमाओं, कैंसलेशन, फ्लशिंग और निरंतर इनपुट के लिए सटीक कॉल अनुक्रमों का दावा (assert) करता है।

उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न

  • डिफ़ॉल्ट्स क्या हैं? यह उत्तर debounce { leading: false, trailing: true } और throttle { leading: true, trailing: true } का उपयोग करता है। अलग-अलग डिफ़ॉल्ट्स अलग-थलग कॉल्स के व्यवहार और टेस्ट्स को बदलते हैं।
  • जब दोनों एज सक्षम हों तो क्या होता है? एक अकेली कॉल केवल leading एज पर इनवोक होती है। Trailing इनवोकेशन केवल तभी होता है जब wait अवधि के दौरान दूसरी कॉल आती है। यह किसी अलग-थलग कार्रवाई को डुप्लिकेट करने से बचाता है।
  • विलंबित कॉल किन arguments और रिसीवर का उपयोग करती है? यह नवीनतम पेंडिंग कॉल के arguments और this का उपयोग करती है। पहले इवेंट को कैप्चर करने से ऑटोकंप्लीट या रिसाइज़ का काम पुराना (stale) हो जाएगा।
  • क्या इनपुट हमेशा के लिए जारी रह सकता है? यदि हाँ, तो केवल trailing वाला debounce कभी नहीं चल सकता है। maxWait पिछले वास्तविक इनवोकेशन या वर्तमान बर्स्ट की शुरुआत से अधिकतम डिले निर्धारित करता है।
  • cancel() और flush() को क्या करना चाहिए? Cancel पेंडिंग काम को हटाता है और बर्स्ट स्टेट को रीसेट करता है। Flush तुरंत एक योग्य trailing कॉल निष्पादित करता है और उसका परिणाम लौटाता है; इसे बाद में डुप्लिकेट कॉल का कारण नहीं बनना चाहिए।
  • क्या सटीक बीता हुआ समय आवश्यक है? कोई भी ब्राउज़र टाइमर सटीक शेड्यूलिंग की गारंटी नहीं देता है। कॉन्ट्रैक्ट सबसे प्रारंभिक पात्रता और क्रम को नियंत्रित करता है; टेस्ट्स शेड्यूलर नॉइज़ को हटाने के लिए एक फेक क्लॉक का उपयोग करते हैं।

30-सेकंड का उत्तर

“मैं पहले एक टाइमलाइन के साथ leading और trailing सिमेंटिक्स को परिभाषित करूँगा। Debounce एक बर्स्ट को समूहीकृत करता है और आम तौर पर किसी अन्य कॉल के बिना wait मिलीसेकंड के बाद इनवोक होता है। Throttle एक बर्स्ट के दौरान समय-समय पर प्रगति की गारंटी देता है। मैं नवीनतम arguments और रिसीवर, अंतिम कॉल समय, अंतिम वास्तविक इनवोकेशन समय, एक टाइमर और नवीनतम परिणाम रखता हूँ।

प्रत्येक कॉल पर, मैं तय करता हूँ कि क्या काम अभी योग्य है। अन्यथा मैं शेष wait शेड्यूल करता हूँ। टाइमर पात्रता की फिर से जाँच करता है क्योंकि बाद की कॉल ने trailing डेडलाइन को आगे बढ़ा दिया हो सकता है। maxWait स्टार्वेशन (भुखमरी) को रोकता है; throttle वही स्टेट मशीन है जिसमें maxWait को wait पर तय किया जाता है। cancel() पेंडिंग स्टेट को साफ़ करता है, और flush() एक पेंडिंग trailing इनवोकेशन निष्पादित करता है। मैं एक फेक क्लॉक के साथ टेस्ट करता हूँ, विशेष रूप से सीमा पर बिल्कुल सटीक कॉल्स, leading प्लस trailing, निरंतर इनपुट, कैंसलेशन, फ्लशिंग और अनमाउंट क्लीनअप।”

चरण-दर-चरण गहन विश्लेषण

चरण 1: शब्दों को टाइमलाइन में बदलें

मान लें wait = 100 ms और कॉल्स A@0, B@40, C@90, और D@220 हैं। केवल-trailing debounce C@190 और D@320 उत्पन्न करता है: प्रत्येक कॉल शांत-अवधि (quiet-period) की डेडलाइन को आगे बढ़ाती है, और नवीनतम argument जीतता है। एक leading-और-trailing debounce A@0, C@190, और D@220 उत्पन्न करता है। D अलग-थलग है, इसलिए यह 320 पर फिर से नहीं चलता है।

एक leading-और-trailing throttle अंतिम पेंडिंग मान को संरक्षित करते हुए प्रति 100 ms विंडो में अधिकतम एक कॉल उत्पन्न करता है। पहले बर्स्ट के लिए, इसका मतलब तुरंत A और trailing सीमा पर नवीनतम पेंडिंग मान C है। वास्तविक ब्राउज़र में सटीक टाइमस्टैम्प वैचारिक सीमा से बाद के हो सकते हैं, लेकिन कभी पहले नहीं।

यह टाइमलाइन स्टेट ट्रांज़िशन को प्रकट करती है: एक निष्क्रिय (idle) कॉल एक बर्स्ट शुरू कर सकती है; बाद की कॉल्स पेंडिंग डेटा को बदल देती हैं; एक टाइमर या तो इनवोक करता है या फिर से शेड्यूल करता है; एक इनवोकेशन पेंडिंग डेटा को साफ़ करता है लेकिन इसके परिणाम को बनाए रखता है। इन ट्रांज़िशन्स को पहले लिखने से अधिकांश ऑफ-बाय-वन और डुप्लिकेट-trailing बग्स से बचा जा सकता है।

चरण 2: एक स्पष्ट स्टेट मशीन लागू करें

नीचे दिया गया कार्यान्वयन बताए गए कॉन्ट्रैक्ट का पालन करता है। shouldInvoke() पहली कॉल, शांत-अवधि की सीमा, क्लॉक के पीछे जाने और maxWait को संभालता है। remainingWait() trailing और अधिकतम-प्रतीक्षा डेडलाइन में से जो पहले हो उसे चुनता है।

js
function debounce(fn, wait, options = {}) {
  wait = Math.max(0, Number(wait) || 0);

  const leading = options.leading === true;
  const trailing = options.trailing !== false;
  const hasMaxWait = Number.isFinite(options.maxWait);
  const maxWait = hasMaxWait
    ? Math.max(wait, options.maxWait)
    : 0;

  let timerId;
  let lastArgs;
  let lastThis;
  let lastCallTime;
  let lastInvokeTime = 0;
  let result;

  function invoke(time) {
    const args = lastArgs;
    const receiver = lastThis;

    lastArgs = undefined;
    lastThis = undefined;
    lastInvokeTime = time;
    result = fn.apply(receiver, args);
    return result;
  }

  function shouldInvoke(time) {
    const sinceCall = time - lastCallTime;
    const sinceInvoke = time - lastInvokeTime;

    return lastCallTime === undefined
      || sinceCall >= wait
      || sinceCall < 0
      || (hasMaxWait && sinceInvoke >= maxWait);
  }

  function remainingWait(time) {
    const sinceCall = time - lastCallTime;
    const trailingWait = wait - sinceCall;

    if (!hasMaxWait) return trailingWait;

    const sinceInvoke = time - lastInvokeTime;
    return Math.min(trailingWait, maxWait - sinceInvoke);
  }

  function trailingEdge(time) {
    timerId = undefined;

    if (trailing && lastArgs) return invoke(time);

    lastArgs = undefined;
    lastThis = undefined;
    return result;
  }

  function timerExpired() {
    const time = Date.now();

    if (shouldInvoke(time)) return trailingEdge(time);

    timerId = setTimeout(timerExpired, remainingWait(time));
  }

  function leadingEdge(time) {
    lastInvokeTime = time;
    timerId = setTimeout(timerExpired, wait);
    return leading ? invoke(time) : result;
  }

  function cancel() {
    if (timerId !== undefined) clearTimeout(timerId);

    timerId = undefined;
    lastArgs = undefined;
    lastThis = undefined;
    lastCallTime = undefined;
    lastInvokeTime = 0;
  }

  function flush() {
    if (timerId === undefined) return result;

    clearTimeout(timerId);
    return trailingEdge(Date.now());
  }

  function debounced(...args) {
    const time = Date.now();
    const invokeNow = shouldInvoke(time);

    lastArgs = args;
    lastThis = this;
    lastCallTime = time;

    if (invokeNow) {
      if (timerId === undefined) return leadingEdge(time);

      if (hasMaxWait) {
        clearTimeout(timerId);
        timerId = setTimeout(timerExpired, wait);
        return invoke(time);
      }
    }

    if (timerId === undefined) {
      timerId = setTimeout(timerExpired, wait);
    }

    return result;
  }

  debounced.cancel = cancel;
  debounced.flush = flush;
  return debounced;
}

function throttle(fn, wait, options = {}) {
  return debounce(fn, wait, {
    leading: options.leading !== false,
    trailing: options.trailing !== false,
    maxWait: wait,
  });
}

स्टेट O(1) है, और प्रत्येक रैपर कॉल O(1) कार्य करती है। कॉलबैक की लागत यूटिलिटी की जटिलता से बाहर है। प्रोडक्शन कोड इसके बजाय एक मेंटेन किए गए कार्यान्वयन को इम्पोर्ट कर सकता है; इंटरव्यू का महत्व इसके कॉन्ट्रैक्ट को समझाने और टेस्ट करने में सक्षम होने में है।

चरण 3: बताएं कि टाइमर दोबारा जांच क्यों करता है

मान लीजिए पहली कॉल 100 ms के लिए एक टाइमर शेड्यूल करती है, फिर दूसरी कॉल 90 ms पर आती है। यदि मूल टाइमर 100 पर आँख बंद करके इनवोक हो जाता है, तो शांत अवधि केवल 10 ms की रह जाती है। इसके बजाय timerExpired() sinceCall की पुनर्गणना करता है, देखता है कि 100 ms की शांति नहीं बीती है, और शेष 90 ms को शेड्यूल करता है।

प्रत्येक इवेंट पर टाइमर को क्लियर और क्रिएट करना एक मान्य सरल केवल-trailing कार्यान्वयन है। दोबारा जांच करने वाली स्टेट मशीन अपनी जटिलता को उचित ठहराती है क्योंकि यह leading कॉल्स, maxWait, रिटर्न वैल्यूज़ और throttle सिमेंटिक्स का भी समर्थन करती है। यदि प्रश्न केवल बुनियादी trailing debounce के लिए पूछता है, तो छोटे समाधान का उपयोग करें और बताएं कि अधिक समृद्ध कॉन्ट्रैक्ट के लिए अधिक स्टेट की आवश्यकता होगी।

चरण 4: maxWait के साथ स्टार्वेशन को रोकें

एक ऑटोकंप्लीट क्वेरी को आमतौर पर एक पॉज़ की प्रतीक्षा करनी चाहिए। इनपुट जारी रहने के दौरान टेलीमेट्री बफर या ऑटोसेव हमेशा के लिए प्रतीक्षा नहीं कर सकता है। wait = 300 ms और maxWait = 1000 ms के साथ, हर 100 ms पर बार-बार की जाने वाली कॉल्स रनटाइम शेड्यूलिंग डिले के अधीन, प्रत्येक 1000 ms की अधिकतम सीमा के आसपास कम से कम एक बार ट्रिगर होती हैं।

maxWait throttle का सेतु भी है। इसे wait के बराबर सेट करने का मतलब है कि निरंतर कॉल्स इनवोकेशन को एक अंतराल से आगे नहीं टाल सकती हैं। एक ही कार्यान्वयन रखने से दो टाइमर स्टेट मशीनों के एज व्यवहार में अंतर आने से बचा जा सकता है। यह निष्पत्ति एक कार्यान्वयन विकल्प है, throttle की एकमात्र मान्य परिभाषा नहीं; कॉन्ट्रैक्ट और टेस्ट्स ही आधिकारिक रहते हैं।

चरण 5: लाइफसाइकल और साइड इफेक्ट्स को संभालें

विलंबित कार्य उस UI से अधिक समय तक जीवित रह सकता है जिसने इसे शेड्यूल किया था। एक कंपोनेंट को क्लीनअप के दौरान cancel() को कॉल करना चाहिए ताकि कोई पुराना कॉलबैक अनमाउंटेड स्टेट को अपडेट न करे, पुराने props का उपयोग न करे, या नेविगेशन के बाद अनुरोध न भेजे। यदि उत्पाद को टीयरडाउन से पहले पेंडिंग टेक्स्ट को कमिट करने की आवश्यकता है, तो जानबूझकर flush() को कॉल करें और फिर क्लीनअप करें; प्रत्येक अनमाउंट पर चुपचाप डेटा सबमिट न करवाएं।

एसिंक्रोनस सर्च को डिबाउंस करना अनुरोध निर्माण को नियंत्रित करता है, प्रतिक्रिया क्रम को नहीं। एक बार अनुरोध शुरू होने के बाद, एक धीमी पुरानी प्रतिक्रिया अभी भी एक नए परिणाम को ओवरराइट कर सकती है। Debounce के अतिरिक्त AbortController, एक अनुरोध जनरेशन, या नवीनतम-प्रतिक्रिया जांच का उपयोग करें। रेट लिमिटिंग और पुरानी प्रतिक्रिया से सुरक्षा अलग-अलग विफलता मोड को हल करते हैं।

चरण 6: वास्तविक कार्य के अनुसार ब्राउज़र प्रिमिटिव चुनें

Debounce का उपयोग तब करें जब केवल स्थिर मान मायने रखता हो, जैसे टाइपिंग रुकने के बाद वैलिडेशन। Throttle का उपयोग तब करें जब मध्यवर्ती प्रगति मायने रखती हो, जैसे पॉइंटर या स्क्रॉल स्थिति का समय-समय पर नमूना लेना। पेंट के साथ विज़ुअल राइट्स को संरेखित करने के लिए requestAnimationFrame() का उपयोग करें, लेकिन यह दावा न करें कि यह स्वचालित रूप से स्क्रॉल इवेंट आवृत्ति को कम करता है। थ्रेशोल्ड-आधारित विजिबिलिटी के लिए IntersectionObserver का उपयोग करें और scrollend का उपयोग तब करें जब आवश्यक इवेंट विशेष रूप से स्क्रॉलिंग का अंत हो।

निर्णय नियम व्यवहार संबंधी है: मध्यवर्ती अवस्थाओं को छोड़ें, आवधिक प्रगति को सुरक्षित रखें, कार्य को पेंट के साथ संरेखित करें, या ब्राउज़र-परिभाषित थ्रेशोल्ड का निरीक्षण करें। आदत से किसी प्रिमिटिव को चुनने से या तो काम व्यर्थ हो सकता है या उपयोगकर्ता-दृश्यमान अपडेट छिप सकते हैं।

चरण 7: वर्चुअल टाइम के साथ टेस्ट करें

Date.now, setTimeout, और clearTimeout को एक फेक क्लॉक से बदलें, या टेस्ट रनर के फेक टाइमर्स का उपयोग करें। मान और वर्चुअल टाइमस्टैम्प रिकॉर्ड करें। प्रत्येक डेडलाइन से ठीक पहले और बिल्कुल उसी समय पर आगे बढ़ें। यह दावा न करें कि एक वास्तविक 100 ms टाइमआउट ठीक 100 ms पर ही फायर होगा।

न्यूनतम मैट्रिक्स में केवल-trailing बर्स्ट्स, केवल-leading बर्स्ट्स, एक और कई कॉल्स के साथ leading प्लस trailing, निरंतर इनपुट के तहत maxWait, बिल्कुल wait पर कॉल, नवीनतम arguments और रिसीवर, परिणाम का पुन: उपयोग, डेडलाइन से पहले cancel, डेडलाइन से पहले flush, बार-बार flush, शून्य प्रतीक्षा, और एक कॉलबैक जो दूसरी रैपर कॉल को शेड्यूल करता है, शामिल हैं। UI एकीकरण के लिए, क्लीनअप और पुराने नेटवर्क प्रतिक्रियाओं का भी परीक्षण करें।

मजबूत नमूना उत्तर

“मैं कोडिंग से पहले एक टाइमलाइन परिभाषित करूँगा। 100 ms trailing debounce के साथ, 0, 40, और 90 पर कॉल्स अंतिम arguments के साथ 190 के बाद पहले योग्य समय पर एक कॉल उत्पन्न करती हैं। एक leading-और-trailing संस्करण 0 और 190 पर कॉल करता है, लेकिन एक अकेली leading कॉल trailing एज पर डुप्लिकेट नहीं होती है।

मेरी स्टेट में एक टाइमर, पेंडिंग arguments और रिसीवर, अंतिम कॉल समय, अंतिम वास्तविक इनवोकेशन समय और नवीनतम परिणाम शामिल हैं। एक टाइमर कभी यह मानकर नहीं चलता कि वह अभी भी योग्य है; वह दोबारा जांच करता है क्योंकि एक नई कॉल शांत डेडलाइन को आगे बढ़ा सकती है। maxWait एक दूसरी डेडलाइन जोड़ता है ताकि निरंतर इनपुट कॉलबैक को स्टार्व (वंचित) न कर सके। Throttle maxWait को wait के बराबर रखकर उसी स्टेट मशीन का पुन: उपयोग करता है।

मैं this को सुरक्षित रखता हूँ, अंतिम इनवोकेशन परिणाम लौटाता हूँ, cancel() में पेंडिंग स्टेट साफ़ करता हूँ, और flush() को अधिकतम एक trailing इनवोकेशन निष्पादित करने देता हूँ। कंपोनेंट में मैं क्लीनअप पर cancel करता हूँ। खोज के लिए, मैं अनुरोधों को अलग से रद्द या वर्शन करता हूँ क्योंकि debounce पुरानी प्रतिक्रियाओं को देर से आने से नहीं रोक सकता। मैं फेक टाइम और सटीक कॉल अनुक्रमों के साथ इस सब को सत्यापित करता हूँ, क्योंकि ब्राउज़र टाइमर देर से चल सकते हैं।”

सामान्य गलतियाँ

  • एज व्यवहार को परिभाषित करने से पहले कोडिंग करना → अलग-अलग उचित कार्यान्वयन अलग-अलग टेस्ट्स में विफल होते हैं → पहले डिफ़ॉल्ट्स और टाइमस्टैम्प्ड कॉल अनुक्रम लिखें।
  • पहले arguments को बनाए रखना → विलंबित कॉलबैक पुराने इनपुट पर कार्य करता है → प्रत्येक कॉल पर पेंडिंग arguments और रिसीवर को बदलें।
  • Leading कॉल के बाद हमेशा trailing कॉल फायर करना → एक क्लिक से दो कार्रवाइयां होती हैं → केवल तभी trail करें जब अंतराल के दौरान कोई अन्य कॉल पेंडिंग हो गई हो।
  • maxWait के बिना हमेशा के लिए रीसेट करना → एक निरंतर स्ट्रीम ऑटोसेव या बैचिंग को रोक सकती है → आवधिक प्रगति की आवश्यकता होने पर अधिकतम डेडलाइन जोड़ें।
  • टाइमर डिले को सटीक मानना → लंबे टास्क्स और रनटाइम थ्रॉटलिंग टाइमस्टैम्प असर्शन्स को तोड़ते हैं → इसे सबसे प्रारंभिक पात्रता मानें और वर्चुअल टाइम के साथ टेस्ट करें।
  • requestAnimationFrame() को सामान्य throttle के रूप में उपयोग करना → यह स्क्रॉल इवेंट्स के समान कैडेंस पर चल सकता है → पेंट संरेखण के लिए इसका उपयोग करें और दर में कमी की आवश्यकता होने पर एक अलग अंतराल मापें।
  • क्लीनअप भूल जाना → विलंबित कार्य नेविगेशन या अनमाउंट के बाद चलता है → लाइफसाइकल क्लीनअप के दौरान cancel करें या जब उत्पाद सिमेंटिक्स की आवश्यकता हो तो स्पष्ट रूप से flush करें।
  • यह मान लेना कि debounce पुराने खोज परिणामों को रोकता है → पहले से शुरू किए गए अनुरोध गलत क्रम में रिज़ॉल्व हो सकते हैं → इसे कैंसलेशन या नवीनतम-अनुरोध वैलिडेशन के साथ संयोजित करें।
  • एक बुनियादी प्रॉम्प्ट के लिए पूरी स्टेट मशीन बनाना → अनावश्यक कोड बग की संभावना बढ़ाता है → बताए गए सबसे छोटे कॉन्ट्रैक्ट को लागू करें, फिर एक्सटेंशन का वर्णन करें।

फॉलो-अप प्रश्न और उत्तर

फॉलो-अप 1: क्या होता है जब कोई कॉल ठीक wait सीमा पर आती है?

क्रम मॉडल को परिभाषित करें। एक डिटरमिनिस्टिक टेस्ट में, यदि पुराना टाइमर टास्क उसी वर्चुअल टाइमस्टैम्प पर नए कॉल टास्क से पहले निष्पादित होता है, तो पुराना बर्स्ट trail हो सकता है और नई कॉल दूसरा बर्स्ट शुरू करती है। यदि नई कॉल को पहले संसाधित किया जाता है, तो यह टाइमर के पुन: जांच करने से पहले पेंडिंग स्टेट को अपडेट कर सकती है। ब्राउज़र टास्क कतारें एक साथ होने वाले बाहरी इवेंट्स को जादुई रूप से एटॉमिक नहीं बनाती हैं। टेस्ट्स को एक विशिष्ट क्रम शेड्यूल करना चाहिए और कार्यान्वयन को आंतरिक रूप से सुसंगत होना चाहिए।

फॉलो-अप 2: setInterval के साथ throttle क्यों लागू नहीं करते?

एक इंटरवल तब भी टिक करता रहता है जब कोई पेंडिंग काम नहीं होता, जब तक कि अतिरिक्त स्टेट इसे दबा न दे। यह leading, अंतिम trailing मान, कैंसलेशन और निष्क्रियता के बाद रीस्टार्ट के बारे में तर्क करना भी कठिन बनाता है। वास्तविक मांग से शेड्यूल किया गया एक वन-शॉट टाइमर अगली सीमा पर स्पष्ट नियंत्रण देता है। setInterval एक सटीक कॉन्ट्रैक्ट के साथ काम कर सकता है, लेकिन सभी आवश्यक सिमेंटिक्स शामिल होने के बाद यह सरल नहीं रह जाता।

फॉलो-अप 3: क्या trailing अक्षम होने पर flush चलना चाहिए?

कोई पेंडिंग trailing कार्य योग्य नहीं है, इसलिए flush() fn को कॉल किए बिना नवीनतम इनवोकेशन परिणाम लौटाता है। यह टाइमर समाप्ति के समान trailing गेट का अनुसरण करता है। यदि किसी उत्पाद को “विकल्पों की परवाह किए बिना निष्पादन बाध्य करें” की आवश्यकता है, तो वह एक अलग API है और उसे एक अलग नाम और टेस्ट्स मिलने चाहिए।

फॉलो-अप 4: आप क्लॉक रोलबैक का परीक्षण कैसे करेंगे?

एक क्लॉक इंजेक्ट करें और उसका मान lastCallTime से नीचे ले जाएं। sinceCall < 0 शाखा एक बड़ा या नकारात्मक शेष समय शेड्यूल करने के बजाय उस स्थिति को योग्य मानती है। जहाँ उपलब्ध हो वहाँ मोनोटोनिक क्लॉक बेहतर है, लेकिन ब्राउज़र यूटिलिटी कॉन्ट्रैक्ट अक्सर रनटाइम क्लॉक का अप्रत्यक्ष रूप से उपयोग करते हैं; रक्षात्मक शाखा रैपर को अटकने से रोकती है।

फॉलो-अप 5: किसी प्रोजेक्ट को इस कार्यान्वयन के बजाय Lodash का उपयोग कब करना चाहिए?

मेंटेन की गई लाइब्रेरी का उपयोग तब करें जब इसके दस्तावेजीकृत सिमेंटिक्स, बंडल रणनीति और प्रोजेक्ट डिपेंडेंसी नीति उपयुक्त हों। एक स्व-निर्मित यूटिलिटी को अपने स्वयं के अनुकूलता परीक्षण, समीक्षा और स्वामित्व की आवश्यकता होती है। एक इंटरव्यू में इसे लागू करना तर्क क्षमता को प्रदर्शित करता है; यह यह साबित नहीं करता कि एक परिपक्व डिपेंडेंसी की नकल करना सबसे अच्छा प्रोडक्शन निर्णय है।

सार्वजनिक स्रोत

संबंधित प्रश्न