पूरे सेशन के आंकड़ों और टेलीमेट्री को भरोसेमंद तरीके से मेज़र करना

वेब की सुविधाएं: fetchLater AbortController और AbortSignal

एनालिटिक्स, Core Web Vitals, और टेलीमेट्री डेटा को भरोसेमंद तरीके से ट्रैक करने के लिए, fetchLater() एपीआई का इस्तेमाल करें. इससे, वेब पेज पर उपयोगकर्ता की पूरी विज़िट को कवर किया जा सकता है, न कि सिर्फ़ पेज लोड होने तक.

fetchLater() एपीआई, किसी सर्वर को डेटा भेजने का सबसे भरोसेमंद तरीका है. इसका इस्तेमाल तब किया जाता है, जब जवाब की ज़रूरत न हो और डेटा को तुरंत डिलीवर करना ज़रूरी न हो. यह उपयोगकर्ता के आंकड़ों, टेलीमेट्री, गड़बड़ी को ट्रैक करने, और परफ़ॉर्मेंस मेट्रिक (जैसे, Core Web Vitals) जैसे डेटा पर लागू होता है.

unload इवेंट लिसनर में <img> पिक्सल बनाने जैसी पुरानी तकनीक, भरोसेमंद नहीं होती है. खास तौर पर, मोबाइल पर. साथ ही, इससे परफ़ॉर्मेंस पर बुरा असर पड़ सकता है. ऐसा इसलिए, क्योंकि इससे पेज, बीएफ़कैश के लिए ज़रूरी शर्तें पूरी नहीं कर पाते.

इन्हें कैसे लागू करें

  1. अनुरोध शेड्यूल करना: काम का डेटा उपलब्ध होते ही, अपने डेटा पेलोड के साथ fetchLater() को कॉल करें. इससे डेटा को बाद में भेजने के लिए सूची में डाल दिया जाता है.

  2. ज़रूरत के हिसाब से पेलोड अपडेट करें: अगर उपयोगकर्ता ज़्यादा डेटा जनरेट करता है या स्थिति बदलती है, तो पहले से शेड्यूल किए गए अनुरोध को रद्द करें. इसके बाद, पूरी तरह से अपडेट किए गए स्नैपशॉट के साथ fetchLater() को फिर से कॉल करें.

  3. बाकी काम ब्राउज़र को करने दें: जब उपयोगकर्ता किसी दूसरे पेज पर जाता है या टैब बंद करता है, तब ब्राउज़र आपके सबसे हाल के fetchLater() कॉल से पेलोड को सुरक्षित तरीके से भेजता है.

कोड का उदाहरण

यह कोड, किसी पेज पर उपयोगकर्ता के सेशन कितनी देर चला, इसे मेज़र करता है. इसके लिए, वह fetchLater() का इस्तेमाल करके, हर 10 सेकंड में सेशन कितनी देर चला, इसकी अपडेट की गई अवधि के साथ एक नया बीकन सूची में जोड़ता है.

const ANALYTICS_ENDPOINT = '/path/to/analytics/endpoint';

const sessionData = {
  duration: 0,
  id: crypto.randomUUID(),
};

let fetchLaterController = null;

function queueBeacon() {
  // Abort any pending beacons before creating a new one.
  if (fetchLaterController) {
    fetchLaterController.abort();
  }
  fetchLaterController = new AbortController();

  // Update session duration to the current page time.
  sessionData.duration = performance.now();

  // Schedule a fetch for the data payload to be sent later.
  // IMPORTANT: wrap the call in a try/catch to handle quota errors.
  try {
    fetchLater(ANALYTICS_ENDPOINT, {
      method: 'POST',
      headers: {'content-type': 'application/json'},
      body: JSON.stringify(sessionData),
      signal: fetchLaterController.signal,
    });
  } catch (error) {
    // Handle errors as needed.
  }
}

// Update the session data and queue a new beacon every 10 seconds.
setInterval(queueBeacon, 10000);

सबसे सही तरीके

  • ज़रूर करें: किसी भी ऐसी स्थिति में सर्वर को डेटा भेजने के लिए fetchLater() का इस्तेमाल करें जहां जवाब ज़रूरी न हो और डेटा को तुरंत भेजना ज़रूरी न हो.
  • ज़रूर इस्तेमाल करें AbortController, ताकि उन मामलों में पेंडिंग फ़ेच को रद्द किया जा सके जहां उपयोगकर्ता के पेज छोड़ने से पहले डेटा को अपडेट करने की ज़रूरत पड़ सकती है
  • ज़रूर करें: पेलोड का साइज़ कम से कम रखें, ताकि कोटा से ज़्यादा न हो. फ़िलहाल, हर ऑरिजिन के लिए यह कोटा करीब 64 केबी है.
  • कोटा से जुड़ी गड़बड़ियों को मैनेज करने के लिए, fetchLater() को किए जाने वाले कॉल को try/catch में रैप करें.
  • globalThis पर fetchLater() की मौजूदगी का पता लगाने के लिए, सुविधा की पहचान करने की प्रोसेस का इस्तेमाल करें. साथ ही, एपीआई के साथ काम न करने वाले ब्राउज़र के लिए, फ़ॉलबैक रणनीति लागू करें.
  • अनुरोध के मुख्य हिस्से के लिए, ReadableStream ऑब्जेक्ट का इस्तेमाल न करें. ऐसा करने पर गड़बड़ी होगी.

ब्राउज़र से जुड़ी सहायता और फ़ॉलबैक रणनीतियां

अगर fetchLater() आपके बेसलाइन टारगेट को पूरा नहीं करता है, तो फ़ॉलबैक रणनीति की ज़रूरत होती है. हालांकि, इस एपीआई की बेहतर विश्वसनीयता और परफ़ॉर्मेंस के फ़ायदों को देखते हुए, अगर ब्राउज़र पर यह एपीआई काम करता है, तो fetchLater() का इस्तेमाल किया जाना चाहिए.

हमारा सुझाव है कि फ़ॉलबैक रणनीति के तौर पर, नीचे दिए गए polyfill का इस्तेमाल करें. यह keepalive या navigator.sendBeacon() के साथ fetch() का इस्तेमाल करके, उन ब्राउज़र को इंटरनल तौर पर मैनेज करता है जो काम नहीं करते. आपके कोड को सीधे fetchLater() को कॉल करना चाहिए. कभी भी fetch(), sendBeacon() या अन्य बीकन एपीआई को खुद कॉल न करें.

fetchLater() polyfill

यहां दिए गए कम से कम fetchLater() पॉलीफ़िल का इस्तेमाल करें. यह उन ब्राउज़र में एपीआई को लागू करता है जो इसे सपोर्ट नहीं करते.

इस पॉलीफ़िल के काम करने के तरीके में सिर्फ़ एक अंतर है. यह उपयोगकर्ता के जाने का पता लगाने के लिए, visibilitychange का इस्तेमाल करता है. यह ब्राउज़र के नेटिव अनलोड हैंडलिंग पर भरोसा नहीं करता. यह अंदरूनी तौर पर लागू होने वाली जानकारी है — आपके कोड को visibilitychange या पेज लाइफ़साइकल के किसी अन्य इवेंट को सुनने की ज़रूरत नहीं है. बस fetchLater() को कॉल करें और पॉलीफ़िल डिलीवरी को हैंडल करता है.

globalThis.fetchLater ??= function fetchLater(url, init = {}) {
  let timeoutHandle;
  let activated = false;

  function sendNow() {
    if (!(init.signal && init.signal.aborted)) {
      // Use fetch keepalive if the browser supports it or if custom fetch
      // parameters are specified (e.g. custom headers or methods).
      // Otherwise fall back to `navigator.sendBeacon()`.
      if (
        'keepalive' in Request.prototype ||
        init.method !== 'POST' ||
        init.headers
      ) {
        fetch(url, Object.assign({}, init, {keepalive: true}));
        activated = true;
      } else {
        activated = navigator.sendBeacon(url, init.body);
      }
    }
    destroy();
  }

  function destroy() {
    document.removeEventListener('visibilitychange', sendNow);
    clearTimeout(timeoutHandle);
  }

  if (document.visibilityState === 'hidden') {
    // If the beacon was created while the page is already hidden, send data
    // ASAP but wait until the next microtask to allow all sync code to run.
    queueMicrotask(sendNow);
  } else {
    document.addEventListener('visibilitychange', sendNow);

    if (typeof init.activateAfter === 'number' && init.activateAfter >= 0) {
      timeoutHandle = setTimeout(sendNow, init.activateAfter);
    }
  }

  if (init.signal) {
    init.signal.addEventListener('abort', destroy);
  }

  return {
    get activated() {
      return activated;
    },
  };
};