ऑफ़लाइन कुकबुक

Service Worker की मदद से, हमने ऑफ़लाइन काम करने की समस्या को हल करने की कोशिश बंद कर दी है. साथ ही, हमने डेवलपर को इस समस्या को खुद हल करने के लिए, ज़रूरी जानकारी दी है. इससे आपको कैश मेमोरी में डेटा सेव करने और अनुरोधों को मैनेज करने का कंट्रोल मिलता है. इसका मतलब है कि आपको अपने हिसाब से पैटर्न बनाने का विकल्प मिलता है. आइए, अलग-अलग पैटर्न पर एक नज़र डालते हैं. हालांकि, आम तौर पर यूआरएल और संदर्भ के आधार पर, इनमें से कई पैटर्न का एक साथ इस्तेमाल किया जाता है.

इनमें से कुछ पैटर्न के काम करने के तरीके के बारे में जानने के लिए, ट्रेनिंग और परफ़ॉर्मेंस पर पड़ने वाले असर के बारे में बताने वाला यह वीडियो देखें.

Service Worker की मदद से, अनुरोधों को कैश मेमोरी में सेव किए बिना मैनेज किया जा सकता है. इसलिए, हम इन दोनों के बारे में अलग-अलग बताएंगे. सबसे पहले, कैश मेमोरी का इस्तेमाल कब किया जाना चाहिए?

इंस्टॉल करने पर—डिपेंडेंसी के तौर पर

इंस्टॉल करने पर - डिपेंडेंसी के तौर पर.
इंस्टॉल करते समय - डिपेंडेंसी के तौर पर.

Service Worker आपको install इवेंट देता है. इसका इस्तेमाल, अन्य इवेंट को मैनेज करने से पहले, ज़रूरी चीज़ों को तैयार करने के लिए किया जा सकता है. ऐसा होने पर, आपके Service Worker का कोई भी पिछला वर्शन अब भी चल रहा है और पेज दिखा रहा है. इसलिए, यहां किए गए बदलावों से उस पर कोई असर नहीं पड़ना चाहिए.

इनके लिए सही है: सीएसएस, इमेज, फ़ॉन्ट, JS, टेंप्लेट… यानी आपकी साइट के उस "वर्शन" के लिए, जो स्टैटिक है.

अगर ये फ़ेच नहीं हो पाते हैं, तो आपकी साइट पूरी तरह से काम नहीं करेगी. ये ऐसी चीज़ें हैं जिन्हें प्लैटफ़ॉर्म के हिसाब से बने ऐप्लिकेशन, शुरुआती डाउनलोड में शामिल करता है.

self.addEventListener('install', function (event) {
  event
.waitUntil(
    caches
.open('mysite-static-v3').then(function (cache) {
     
return cache.addAll([
       
'/css/whatever-v3.css',
       
'/css/imgs/sprites-v6.png',
       
'/css/fonts/whatever-v8.woff',
       
'/js/all-min-v4.js',
       
// etc.
     
]);
   
}),
 
);
});

event.waitUntil, इंस्टॉल होने में लगने वाले समय और इंस्टॉल होने की स्थिति की जानकारी देता है. अगर प्रॉमिस को अस्वीकार कर दिया जाता है, तो इंस्टॉलेशन को गड़बड़ी माना जाता है और इस सर्विस वर्कर को छोड़ दिया जाएगा. अगर कोई पुराना वर्शन चल रहा है, तो उसे छोड़ दिया जाएगा. caches.open() और cache.addAll() सामान लौटाने के वादे. अगर कोई भी संसाधन फ़ेच नहीं हो पाता है, तो cache.addAll() कॉल अस्वीकार कर दिया जाता है.

trained-to-thrill पर, इसका इस्तेमाल स्टैटिक ऐसेट को कैश मेमोरी में सेव करने के लिए किया जाता है.

इंस्टॉल करने पर—डिपेंडेंसी के तौर पर नहीं

इंस्टॉल करने पर - डिपेंडेंसी के तौर पर नहीं.
इंस्टॉल करने पर - डिपेंडेंसी के तौर पर नहीं.

यह ऊपर बताए गए तरीके से मिलता-जुलता है. हालांकि, इससे इंस्टॉल होने में लगने वाला समय नहीं बढ़ेगा. साथ ही, कैश मेमोरी सेव करने में कोई गड़बड़ी होने पर भी, इंस्टॉल पूरा हो जाएगा.

इनके लिए सही है: बड़े ऐसेट, जिनकी तुरंत ज़रूरत नहीं होती. जैसे, किसी गेम के अगले लेवल के लिए ऐसेट.

self.addEventListener('install', function (event) {
  event
.waitUntil(
    caches
.open('mygame-core-v1').then(function (cache) {
      cache
       
.addAll
       
// levels 11–20
       
();
     
return cache
       
.addAll
       
// core assets and levels 1–10
       
();
   
}),
 
);
});

ऊपर दिए गए उदाहरण में, cache.addAll से event.waitUntil के लिए, 11 से 20 लेवल के गेम को ऑफ़लाइन उपलब्ध कराने का वादा नहीं किया गया है. इसलिए, भले ही यह वादा पूरा न हो पाए, फिर भी गेम ऑफ़लाइन उपलब्ध रहेगा. हालांकि, आपको उन लेवल के मौजूद न होने की संभावना को ध्यान में रखना होगा. अगर वे मौजूद नहीं हैं, तो उन्हें फिर से कैश मेमोरी में सेव करने की कोशिश करें.

लेवल 11 से 20 डाउनलोड होने के दौरान, Service Worker को बंद किया जा सकता है. ऐसा इसलिए, क्योंकि इवेंट मैनेज करने की प्रोसेस पूरी हो चुकी है. इसका मतलब है कि इवेंट कैश मेमोरी में सेव नहीं किए जाएंगे. आने वाले समय में, वेब पीरियडिक बैकग्राउंड सिंक एपीआई, इस तरह के मामलों और फ़िल्मों जैसे बड़े डाउनलोड को मैनेज करेगा. फ़िलहाल, यह एपीआई सिर्फ़ Chromium के फ़ॉर्क पर काम करता है.

चालू होने पर

चालू होने पर.
चालू करें.

इनके लिए सही है: क्लीन-अप और माइग्रेशन.

नया सर्विस वर्कर इंस्टॉल होने और पुराने वर्शन का इस्तेमाल न होने पर, नया वर्शन चालू हो जाता है और आपको activate इवेंट मिलता है. पुराना वर्शन हट जाने के बाद, IndexedDB में स्कीमा माइग्रेशन को मैनेज करने का यह सही समय है. साथ ही, इस्तेमाल न किए गए कैश मेमोरी को भी मिटाया जा सकता है.

self.addEventListener('activate', function (event) {
  event
.waitUntil(
    caches
.keys().then(function (cacheNames) {
     
return Promise.all(
        cacheNames
         
.filter(function (cacheName) {
           
// Return true if you want to remove this cache,
           
// but remember that caches are shared across
           
// the whole origin
         
})
         
.map(function (cacheName) {
           
return caches.delete(cacheName);
         
}),
     
);
   
}),
 
);
});

चालू करने के दौरान, fetch जैसे अन्य इवेंट को एक सूची में रखा जाता है. इसलिए, चालू होने में ज़्यादा समय लगने पर, पेज लोड होने में रुकावट आ सकती है. ऐक्टिवेशन को ज़्यादा से ज़्यादा आसान बनाएं और इसका इस्तेमाल सिर्फ़ उन कामों के लिए करें जिन्हें पुराने वर्शन के चालू होने पर नहीं किया जा सकता था.

trained-to-thrill पर, इसका इस्तेमाल पुराने कैश मेमोरी हटाने के लिए किया जाता है.

उपयोगकर्ता के इंटरैक्शन पर

उपयोगकर्ता के इंटरैक्शन पर.
उपयोगकर्ता के इंटरैक्शन पर.

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

उपयोगकर्ता को "बाद में पढ़ें" या "ऑफ़लाइन के लिए सेव करें" बटन दें. क्लिक करने पर, नेटवर्क से अपनी ज़रूरत के मुताबिक कॉन्टेंट फ़ेच करें और उसे कैश मेमोरी में डालें.

document.querySelector('.cache-article').addEventListener('click', function (event) {
  event
.preventDefault();

 
var id = this.dataset.articleId;
  caches
.open('mysite-article-' + id).then(function (cache) {
    fetch
('/get-article-urls?id=' + id)
     
.then(function (response) {
       
// /get-article-urls returns a JSON-encoded array of
       
// resource URLs that a given article depends on
       
return response.json();
     
})
     
.then(function (urls) {
        cache
.addAll(urls);
     
});
 
});
});

कैश मेमोरी एपीआई, पेजों के साथ-साथ सर्विस वर्कर के लिए भी उपलब्ध है. इसका मतलब है कि सीधे पेज से कैश मेमोरी में जोड़ा जा सकता है.

नेटवर्क रिस्पॉन्स पर

नेटवर्क के जवाब पर.
नेटवर्क के जवाब पर.

इनके लिए सही है: अक्सर अपडेट होने वाले संसाधन, जैसे कि उपयोगकर्ता का इनबॉक्स या लेख का कॉन्टेंट. यह सुविधा, अवतार जैसे ग़ैर-ज़रूरी कॉन्टेंट के लिए भी काम की है. हालांकि, इसका इस्तेमाल करते समय सावधानी बरतने की ज़रूरत है.

अगर कोई अनुरोध कैश मेमोरी में मौजूद किसी भी चीज़ से मेल नहीं खाता है, तो उसे नेटवर्क से पाएं, पेज पर भेजें, और एक ही समय पर उसे कैश मेमोरी में जोड़ें.

अगर आपको अवतार जैसे कई यूआरएल के लिए ऐसा करना है, तो आपको ध्यान रखना होगा कि आपके ओरिजिन के स्टोरेज में जगह खाली रहे. अगर उपयोगकर्ता को डिस्क में जगह खाली करनी है, तो आपको मुख्य उम्मीदवार नहीं बनना चाहिए. पक्का करें कि आपने कैश मेमोरी में से ऐसे आइटम हटा दिए हों जिनकी अब आपको ज़रूरत नहीं है.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.open('mysite-dynamic').then(function (cache) {
     
return cache.match(event.request).then(function (response) {
       
return (
          response
||
          fetch
(event.request).then(function (response) {
            cache
.put(event.request, response.clone());
           
return response;
         
})
       
);
     
});
   
}),
 
);
});

मेमोरी का बेहतर तरीके से इस्तेमाल करने के लिए, जवाब/अनुरोध के मुख्य हिस्से को सिर्फ़ एक बार पढ़ा जा सकता है. ऊपर दिया गया कोड, .clone() का इस्तेमाल करके अतिरिक्त कॉपी बनाता है, जिन्हें अलग से पढ़ा जा सकता है.

trained-to-thrill पर, इसका इस्तेमाल Flickr इमेज को कैश मेमोरी में सेव करने के लिए किया जाता है.

Stale-while-revalidate

Stale-while-revalidate.
Stale-while-revalidate.

इनके लिए सही है: ऐसे रिसॉर्स जिन्हें बार-बार अपडेट करना पड़ता है और जिनके लिए सबसे नए वर्शन की ज़रूरत नहीं होती. अवतार इस कैटगरी में आ सकते हैं.

अगर कैश मेमोरी में सेव किया गया कोई वर्शन उपलब्ध है, तो उसका इस्तेमाल करें. हालांकि, अगली बार अपडेट फ़ेच करें.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.open('mysite-dynamic').then(function (cache) {
     
return cache.match(event.request).then(function (response) {
       
var fetchPromise = fetch(event.request).then(function (networkResponse) {
          cache
.put(event.request, networkResponse.clone());
         
return networkResponse;
       
});
       
return response || fetchPromise;
     
});
   
}),
 
);
});

यह एचटीटीपी के stale-while-revalidate से काफ़ी मिलता-जुलता है.

पुश मैसेज पर

पुश मैसेज पर.
पुश मैसेज पर.

Push API, Service Worker के साथ काम करने वाली एक और सुविधा है. इससे, ओएस की मैसेज सेवा से मिले मैसेज के जवाब में, Service Worker को जगाया जा सकता है. ऐसा तब भी होता है, जब उपयोगकर्ता के पास आपकी साइट का कोई टैब न खुला हो. सिर्फ़ सर्विस वर्कर को जगाया जाता है. इसके लिए, आपको किसी पेज से अनुमति का अनुरोध करना होगा. इसके बाद, उपयोगकर्ता से अनुमति मांगी जाएगी.

इसका इस्तेमाल इनके लिए किया जा सकता है: सूचना से जुड़ा कॉन्टेंट, जैसे कि चैट मैसेज, ताज़ा खबर या ईमेल. इसके अलावा, ऐसा कॉन्टेंट भी तुरंत सिंक किया जा सकता है जो अक्सर नहीं बदलता. जैसे, 'क्या-क्या करें' सूची में बदलाव या कैलेंडर में बदलाव.

आम तौर पर, आखिर में एक सूचना दिखती है. इस पर टैप करने पर, काम का पेज खुलता है या उस पर फ़ोकस होता है. हालांकि, ऐसा होने से पहले कैश मेमोरी को अपडेट करना बहुत ज़रूरी है. पुश मैसेज मिलने के समय, उपयोगकर्ता ज़रूर ऑनलाइन होता है. हालांकि, हो सकता है कि वह सूचना के साथ इंटरैक्ट करते समय ऑनलाइन न हो. इसलिए, यह ज़रूरी है कि इस कॉन्टेंट को ऑफ़लाइन उपलब्ध कराया जाए.

यह कोड, सूचना दिखाने से पहले कैश मेमोरी को अपडेट करता है:

self.addEventListener('push', function (event) {
 
if (event.data.text() == 'new-email') {
    event
.waitUntil(
      caches
       
.open('mysite-dynamic')
       
.then(function (cache) {
         
return fetch('/inbox.json').then(function (response) {
            cache
.put('/inbox.json', response.clone());
           
return response.json();
         
});
       
})
       
.then(function (emails) {
          registration
.showNotification('New email', {
            body
: 'From ' + emails[0].from.name,
            tag
: 'new-email',
         
});
       
}),
   
);
 
}
});

self
.addEventListener('notificationclick', function (event) {
 
if (event.notification.tag == 'new-email') {
   
// Assume that all of the resources needed to render
   
// /inbox/ have previously been cached, e.g. as part
   
// of the install handler.
   
new WindowClient('/inbox/');
 
}
});

बैकग्राउंड सिंक करने की सुविधा चालू है

बैकग्राउंड सिंक चालू है.
बैकग्राउंड में सिंक करने की सुविधा चालू हो.

बैकग्राउंड में सिंक करने की सुविधा, Service Worker के साथ काम करने वाली एक और सुविधा है. इसकी मदद से, बैकग्राउंड डेटा सिंक करने का अनुरोध एक बार या किसी तय समयसीमा के हिसाब से किया जा सकता है. ऐसा तब भी होता है, जब उपयोगकर्ता के पास आपकी साइट का कोई टैब खुला न हो. सिर्फ़ सर्विस वर्कर को जगाया जाता है. इसके लिए, किसी पेज से अनुमति का अनुरोध किया जाता है और उपयोगकर्ता से अनुमति मांगी जाती है.

इनके लिए सही है: ऐसे अपडेट जो ज़रूरी नहीं हैं. खास तौर पर, ऐसे अपडेट जो नियमित तौर पर होते हैं और जिनके लिए हर अपडेट पर पुश मैसेज भेजना उपयोगकर्ताओं के लिए बहुत ज़्यादा होगा. जैसे, सोशल मीडिया टाइमलाइन या समाचार लेख.

self.addEventListener('sync', function (event) {
 
if (event.id == 'update-leaderboard') {
    event
.waitUntil(
      caches
.open('mygame-dynamic').then(function (cache) {
       
return cache.add('/leaderboard.json');
     
}),
   
);
 
}
});

कैश मेमोरी में डेटा सेव करना

आपके ऑरिजिन को कुछ खाली जगह दी जाती है, ताकि वह अपनी ज़रूरत के हिसाब से उसका इस्तेमाल कर सके. यह खाली जगह, सभी ऑरिजिन स्टोरेज के बीच शेयर की जाती है: (लोकल) स्टोरेज, IndexedDB, फ़ाइल सिस्टम ऐक्सेस, और कैश मेमोरी.

आपको मिलने वाली रकम तय नहीं है. यह डिवाइस और स्टोरेज की स्थिति के हिसाब से अलग-अलग होगा. आपको कितने क्रेडिट मिले हैं, यह पता करने के लिए:

if (navigator.storage && navigator.storage.estimate) {
 
const quota = await navigator.storage.estimate();
 
// quota.usage -> Number of bytes used.
 
// quota.quota -> Maximum number of bytes available.
 
const percentageUsed = (quota.usage / quota.quota) * 100;
  console
.log(`You've used ${percentageUsed}% of the available storage.`);
 
const remaining = quota.quota - quota.usage;
  console
.log(`You can write up to ${remaining} more bytes.`);
}

हालांकि, डिवाइस का स्टोरेज भर जाने पर, ब्राउज़र आपका डेटा मिटा सकता है. यह ब्राउज़र के स्टोरेज के लिए भी लागू होता है. माफ़ करें, ब्राउज़र उन फ़िल्मों और गेम के बीच फ़र्क़ नहीं कर सकता जिन्हें आपको किसी भी कीमत पर अपने कलेक्शन में रखना है और जिन्हें आपको नहीं रखना है.

इस समस्या को हल करने के लिए, StorageManager इंटरफ़ेस का इस्तेमाल करें:

// From a page:
navigator
.storage.persist()
.then(function(persisted) {
 
if (persisted) {
   
// Hurrah, your data is here to stay!
 
} else {
   
// So sad, your data may get chucked. Sorry.
});

हालांकि, इसके लिए उपयोगकर्ता को अनुमति देनी होगी. इसके लिए, Permissions API का इस्तेमाल करें.

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

इसके काम करने के लिए, ऑपरेटिंग सिस्टम को "ड्यूरेबल" ऑरिजिन को स्टोरेज के इस्तेमाल के ब्रेकडाउन में, प्लैटफ़ॉर्म के हिसाब से ऐप्लिकेशन के बराबर मानना होगा. इसके बजाय, ब्राउज़र को एक आइटम के तौर पर रिपोर्ट करना होगा.

सुझाव दिखाना—अनुरोधों का जवाब देना

इससे कोई फ़र्क़ नहीं पड़ता कि आपने कितनी कैश मेमोरी सेव की है. जब तक आप उसे यह नहीं बताते कि कैश मेमोरी का इस्तेमाल कब और कैसे करना है, तब तक सेवा वर्कर इसका इस्तेमाल नहीं करेगा. अनुरोधों को मैनेज करने के कुछ पैटर्न यहां दिए गए हैं:

सिर्फ़ कैश मेमोरी

सिर्फ़ कैश मेमोरी.
सिर्फ़ कैश मेमोरी.

इनके लिए सही है: अपनी साइट के किसी खास "वर्शन" के लिए, स्टैटिक कॉन्टेंट. आपको इन्हें इंस्टॉल इवेंट में कैश मेमोरी में सेव करना चाहिए, ताकि आप इन पर भरोसा कर सकें.

self.addEventListener('fetch', function (event) {
 
// If a match isn't found in the cache, the response
 
// will look like a connection error
  event
.respondWith(caches.match(event.request));
});

हालांकि, आपको अक्सर इस मामले को खास तौर पर मैनेज करने की ज़रूरत नहीं पड़ती, कैश मेमोरी, नेटवर्क पर वापस जाना इसे कवर करता है.

सिर्फ़ नेटवर्क

सिर्फ़ नेटवर्क.
सिर्फ़ नेटवर्क.

इनके लिए सही है: ऐसी चीज़ें जिनका ऑफ़लाइन कोई विकल्प नहीं है. जैसे, आंकड़े पाने के लिए पिंग, नॉन-GET अनुरोध.

self.addEventListener('fetch', function (event) {
  event
.respondWith(fetch(event.request));
 
// or simply don't call event.respondWith, which
 
// will result in default browser behavior
});

हालांकि, आपको अक्सर इस मामले को खास तौर पर मैनेज करने की ज़रूरत नहीं पड़ती, कैश मेमोरी, नेटवर्क पर वापस जाना इसे कवर करता है.

कैश मेमोरी, नेटवर्क पर वापस जाना

कैश मेमोरी का इस्तेमाल करके, नेटवर्क से कनेक्ट किया जा रहा है.
कैश मेमोरी, नेटवर्क पर वापस आना.

इनके लिए सही है: ऑफ़लाइन-फ़र्स्ट ऐप्लिकेशन बनाना. ऐसे मामलों में, ज़्यादातर अनुरोधों को इस तरह मैनेज किया जा सकता है. आने वाले अनुरोध के आधार पर, अन्य पैटर्न अपवाद होंगे.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.match(event.request).then(function (response) {
     
return response || fetch(event.request);
   
}),
 
);
});

इससे आपको कैश मेमोरी में मौजूद आइटम के लिए "सिर्फ़ कैश मेमोरी" और कैश मेमोरी में मौजूद आइटम के लिए "सिर्फ़ नेटवर्क" व्यवहार की जानकारी मिलती है. इसमें ऐसे सभी अनुरोध शामिल होते हैं जो GET नहीं होते, क्योंकि उन्हें कैश मेमोरी में सेव नहीं किया जा सकता.

कैश और नेटवर्क रेस

कैश और नेटवर्क रेस.
कैश मेमोरी और नेटवर्क रेस.

इनके लिए सही है: छोटी ऐसेट, जहां आपको डिस्क के धीमे ऐक्सेस वाले डिवाइसों पर परफ़ॉर्मेंस चाहिए.

पुरानी हार्ड ड्राइव, वायरस स्कैनर, और तेज़ इंटरनेट कनेक्शन के कुछ कॉम्बिनेशन की मदद से, डिस्क पर मौजूद संसाधनों को नेटवर्क से ज़्यादा तेज़ी से पाया जा सकता है. हालांकि, जब उपयोगकर्ता के पास डिवाइस पर कॉन्टेंट मौजूद हो, तब नेटवर्क का इस्तेमाल करना डेटा की बर्बादी हो सकती है. इसलिए, इस बात का ध्यान रखें.

// Promise.race is no good to us because it rejects if
// a promise rejects before fulfilling. Let's make a proper
// race function:
function promiseAny(promises) {
 
return new Promise((resolve, reject) => {
   
// make sure promises are all promises
    promises
= promises.map((p) => Promise.resolve(p));
   
// resolve this promise as soon as one resolves
    promises
.forEach((p) => p.then(resolve));
   
// reject if all promises reject
    promises
.reduce((a, b) => a.catch(() => b)).catch(() => reject(Error('All failed')));
 
});
}

self
.addEventListener('fetch', function (event) {
  event
.respondWith(promiseAny([caches.match(event.request), fetch(event.request)]));
});

नेटवर्क की कैश मेमोरी का इस्तेमाल करना

नेटवर्क, कैश मेमोरी का इस्तेमाल कर रहा है.
नेटवर्क की समस्या होने पर, कैश मेमोरी का इस्तेमाल किया जा रहा है.

इनके लिए सही है: साइट के "वर्शन" के बाहर, अक्सर अपडेट होने वाले संसाधनों के लिए तुरंत ठीक करने का तरीका. उदाहरण के लिए, लेख, अवतार, सोशल मीडिया टाइमलाइन, और गेम के लीडर बोर्ड.

इसका मतलब है कि ऑनलाइन उपयोगकर्ताओं को सबसे अप-टू-डेट कॉन्टेंट दिया जाता है, लेकिन ऑफ़लाइन उपयोगकर्ताओं को कैश मेमोरी में सेव किया गया पुराना वर्शन मिलता है. अगर नेटवर्क अनुरोध पूरा हो जाता है, तो हो सकता है कि आप कैश मेमोरी एंट्री को अपडेट करना चाहें.

हालांकि, इस तरीके में कुछ समस्याएं हैं. अगर उपयोगकर्ता के डिवाइस का इंटरनेट कनेक्शन कभी-कभी काम नहीं करता या धीमा है, तो उसे अपने डिवाइस पर पहले से मौजूद कॉन्टेंट को देखने के लिए, नेटवर्क के काम न करने का इंतज़ार करना होगा. इसमें काफ़ी समय लग सकता है और उपयोगकर्ता को परेशानी हो सकती है. बेहतर समाधान के लिए, अगला पैटर्न, कैश मेमोरी के बाद नेटवर्क देखें.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    fetch
(event.request).catch(function () {
     
return caches.match(event.request);
   
}),
 
);
});

कैश मेमोरी में सेव करने के बाद नेटवर्क से डाउनलोड करना

कैश मेमोरी में सेव करने के बाद, नेटवर्क से डाउनलोड करें.
कैश मेमोरी में सेव करें, फिर नेटवर्क से डाउनलोड करें.

इसका इस्तेमाल इनके लिए किया जा सकता है: ऐसा कॉन्टेंट जो बार-बार अपडेट होता है. उदाहरण के लिए, लेख, सोशल मीडिया टाइमलाइन, और गेम के लीडरबोर्ड.

इसके लिए, पेज को दो अनुरोध करने होंगे. एक कैश मेमोरी में और दूसरा नेटवर्क से. इसका मकसद, पहले कैश मेमोरी में सेव किया गया डेटा दिखाना है. इसके बाद, नेटवर्क डेटा मिलने पर/अगर मिलता है, तो पेज को अपडेट करना है.

कभी-कभी, नया डेटा (जैसे, गेम लीडरबोर्ड) आने पर, मौजूदा डेटा को बदला जा सकता है. हालांकि, इससे बड़े कॉन्टेंट में रुकावट आ सकती है. आम तौर पर, किसी ऐसी चीज़ को "गायब" न करें जिसे उपयोगकर्ता पढ़ रहा हो या जिससे इंटरैक्ट कर रहा हो.

Twitter, पुराने कॉन्टेंट के ऊपर नया कॉन्टेंट जोड़ता है और स्क्रोल की पोज़िशन में बदलाव करता है, ताकि उपयोगकर्ता को कोई रुकावट न आए. ऐसा इसलिए होता है, क्योंकि Twitter पर कॉन्टेंट का क्रम ज़्यादातर लीनियर होता है. मैंने ट्रेन्ड के हिसाब से इस पैटर्न को कॉपी किया, ताकि स्क्रीन पर कॉन्टेंट जल्द से जल्द दिखे. साथ ही, नया कॉन्टेंट मिलते ही उसे दिखाया जा सके.

पेज में कोड:

var networkDataReceived = false;

startSpinner
();

// fetch fresh data
var networkUpdate = fetch('/data.json')
 
.then(function (response) {
   
return response.json();
 
})
 
.then(function (data) {
    networkDataReceived
= true;
    updatePage
(data);
 
});

// fetch cached data
caches
 
.match('/data.json')
 
.then(function (response) {
   
if (!response) throw Error('No data');
   
return response.json();
 
})
 
.then(function (data) {
   
// don't overwrite newer network data
   
if (!networkDataReceived) {
      updatePage
(data);
   
}
 
})
 
.catch(function () {
   
// we didn't get cached data, the network is our last hope:
   
return networkUpdate;
 
})
 
.catch(showErrorMessage)
 
.then(stopSpinner);

सर्विस वर्कर में कोड:

आपको हमेशा नेटवर्क पर जाकर, कैश मेमोरी को अपडेट करना चाहिए.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
    caches
.open('mysite-dynamic').then(function (cache) {
     
return fetch(event.request).then(function (response) {
        cache
.put(event.request, response.clone());
       
return response;
     
});
   
}),
 
);
});

trained-to-thrill में, मैंने इस समस्या को हल करने के लिए, फ़ेच के बजाय XHR का इस्तेमाल किया. साथ ही, Accept हेडर का गलत इस्तेमाल करके, सर्विस वर्कर को यह बताया कि नतीजा कहां से पाना है (पेज कोड, सर्विस वर्कर कोड).

सामान्य फ़ॉलबैक

सामान्य फ़ॉलबैक.
सामान्य फ़ॉलबैक.

अगर कैश मेमोरी और/या नेटवर्क से कोई कॉन्टेंट नहीं मिलता है, तो कोई सामान्य फ़ॉलबैक दिया जा सकता है.

इनके लिए सही है: अवतार, पोस्ट करने के अनुरोधों के अस्वीकार होने, और "ऑफ़लाइन होने पर उपलब्ध नहीं" पेज जैसी दूसरी इमेज.

self.addEventListener('fetch', function (event) {
  event
.respondWith(
   
// Try the cache
    caches
     
.match(event.request)
     
.then(function (response) {
       
// Fall back to network
       
return response || fetch(event.request);
     
})
     
.catch(function () {
       
// If both fail, show a generic fallback:
       
return caches.match('/offline.html');
       
// However, in reality you'd have many different
       
// fallbacks, depending on URL and headers.
       
// Eg, a fallback silhouette image for avatars.
     
}),
 
);
});

आपके फ़ॉलबैक आइटम की संभावना इंस्टॉल डिपेंडेंसी है.

अगर आपका पेज कोई ईमेल पोस्ट कर रहा है, तो हो सकता है कि आपका सेवा वर्कर, ईमेल को IndexedDB के 'आउटबॉक्स' में सेव कर दे. साथ ही, पेज को यह जानकारी दे कि ईमेल भेजने में समस्या हुई, लेकिन डेटा को सेव कर लिया गया है.

सेवा वर्कर-साइड टेंप्लेट

ServiceWorker-साइड टेंप्लेट.
ServiceWorker-साइड टेंप्लेट.

इनके लिए सही है: ऐसे पेज जिनके सर्वर रिस्पॉन्स को कैश मेमोरी में सेव नहीं किया जा सकता.

सर्वर पर पेजों को रेंडर करने से, पेज तेज़ी से लोड होते हैं.हालांकि, इसका मतलब यह हो सकता है कि स्टेटस का ऐसा डेटा शामिल किया जाए जो कैश मेमोरी में काम का न हो. उदाहरण के लिए, "इस तौर पर लॉग इन किया गया है…". अगर आपके पेज को किसी सर्विस वर्कर से कंट्रोल किया जाता है, तो टेंप्लेट के साथ-साथ JSON डेटा का अनुरोध किया जा सकता है और उसे रेंडर किया जा सकता है.

importScripts('templating-engine.js');

self
.addEventListener('fetch', function (event) {
 
var requestURL = new URL(event.request.url);

  event
.respondWith(
   
Promise.all([
      caches
.match('/article-template.html').then(function (response) {
       
return response.text();
     
}),
      caches
.match(requestURL.path + '.json').then(function (response) {
       
return response.json();
     
}),
   
]).then(function (responses) {
     
var template = responses[0];
     
var data = responses[1];

     
return new Response(renderTemplate(template, data), {
        headers
: {
         
'Content-Type': 'text/html',
       
},
     
});
   
}),
 
);
});

सभी जानकारी को एक साथ जोड़ना

इनमें से किसी भी तरीके का इस्तेमाल किया जा सकता है. असल में, अनुरोध के यूआरएल के आधार पर, आपको इनमें से कई का इस्तेमाल करना पड़ सकता है. उदाहरण के लिए, थ्रिल देने के लिए ट्रेन किया गया एआई इनका इस्तेमाल करता है:

अनुरोध देखें और तय करें कि क्या करना है:

self.addEventListener('fetch', function (event) {
 
// Parse the URL:
 
var requestURL = new URL(event.request.url);

 
// Handle requests to a particular host specifically
 
if (requestURL.hostname == 'api.example.com') {
    event
.respondWith(/* some combination of patterns */);
   
return;
 
}
 
// Routing for local URLs
 
if (requestURL.origin == location.origin) {
   
// Handle article URLs
   
if (/^\/article\//.test(requestURL.pathname)) {
      event
.respondWith(/* some other combination of patterns */);
     
return;
   
}
   
if (/\.webp$/.test(requestURL.pathname)) {
      event
.respondWith(/* some other combination of patterns */);
     
return;
   
}
   
if (request.method == 'POST') {
      event
.respondWith(/* some other combination of patterns */);
     
return;
   
}
   
if (/cheese/.test(requestURL.pathname)) {
      event
.respondWith(
       
new Response('Flagrant cheese error', {
          status
: 512,
       
}),
     
);
     
return;
   
}
 
}

 
// A sensible default pattern
  event
.respondWith(
    caches
.match(event.request).then(function (response) {
     
return response || fetch(event.request);
   
}),
 
);
});

…आपको यह समझ आ गया होगा.

क्रेडिट

…इन खूबसूरत आइकॉन के लिए:

साथ ही, जेफ़ पॉस्निक को धन्यवाद, जिन्होंने "पब्लिश करें" पर क्लिक करने से पहले, कई गड़बड़ियों का पता लगाया.

इसके बारे में और पढ़ें