איך לסנכרן נתונים מדי פעם ברקע

Cecilia Cong
Cecilia Cong

הדרך המודרנית

סנכרון תקופתי ברקע מאפשר להציג תוכן חדש בהשקה של דף Progressive Web App או שירות בגיבוי Worker. היא עושה זאת על ידי הורדת נתונים ברקע, כשלא נעשה שימוש באפליקציה או בדף.

שימוש בממשק ה-API של סנכרון תקופתי ברקע

אחרי שמתקינים את קובץ השירות (service worker), משתמשים ב-Permissions API כדי לשלוח שאילתות לגבי periodic-background-sync. אפשר לעשות זאת מהקשר של חלון או של קובץ שירות (service worker).

const status = await navigator.permissions.query({
  name: 'periodic-background-sync',
});
if (status.state === 'granted') {
  // Periodic background sync can be used.
} else {
  // Periodic background sync cannot be used.
}

רישום סנכרון תקופתי מחייב גם תג וגם מרווח זמן מינימלי לסנכרון (minInterval). התג מזהה את הסנכרון הרשום כך שניתן יהיה לרשום מספר סנכרונים. בדוגמה הבאה, שם התג הוא 'content-sync' ו-minInterval הוא יום אחד.

navigator.serviceWorker.ready.then(async registration => {
  try {
    await registration.periodicSync.register('get-cats', { minInterval: 24 * 60 * 60 * 1000 });
    console.log(Periodic background sync registered.');
  } catch (err) {
    console.error(err.name, err.message);
  }
});

קוראים לפונקציה periodicSync.getTags() כדי לאחזר מערך של תגי רישום. בדוגמה הבאה נעשה שימוש בשמות של תגים כדי לאשר שעדכון המטמון פעיל על מנת להימנע מעדכונים נוספים.

const registration = await navigator.serviceWorker.ready;
if ('periodicSync' in registration) {
  const tags = await registration.periodicSync.getTags();
  // Only update content if sync isn't set up.
  if (!tags.includes('content-sync')) {
    updateContentOnPageLoad();
  }
} else {
  // If periodic background sync isn't supported, always update.
  updateContentOnPageLoad();
}

כדי להגיב לאירוע סנכרון ברקע תקופתי, צריך להוסיף handler של אירועים של periodicsync ל-service worker. אובייקט האירוע שמועבר אליו יכיל פרמטר של תג שתואם לערך שנעשה בו שימוש במהלך הרישום. לדוגמה, אם נרשם סנכרון תקופתי ברקע בשם 'content-sync', הערך של event.tag יהיה 'content-sync'.

self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'content-sync') {
    event.waitUntil(syncContent());
  }
});

תאימות דפדפן

תמיכה בדפדפן

  • 80
  • 80
  • x
  • x

מקור

הדרך הקלאסית

במקום לעדכן את הנתונים ברקע כך שיהיו מוכנים כשהמשתמש יטען את האפליקציה, הדרך הקלאסית פשוט לעדכן את הנתונים בטעינה.

קריאה נוספת

הדגמה (דמו)

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="icon"
      href=""
    />
    <link rel="manifest" href="./manifest.json" />
    <title>How to periodically synchronize data in the background</title>
    <link rel="stylesheet" href="/style.css" />
    <!-- TODO: Devsite - Removed inline handlers -->
    <!-- <script src="/script.js" defer></script> -->
  </head>
  <body>
    <h1>How to periodically synchronize data in the background</h1>
    <p class="available">Periodic background sync can be used. Install the app first.</p>
    <p class="not-available">Periodic background sync cannot be used.</p>
    <h2>Last updated</h2>
    <p class="last-updated">Never</p>
    <h2>Registered tags</h2>
    <ul>
      <li>None yet.</li>
    </ul>
  </body>
</html>

CSS


        html {
  box-sizing: border-box;
  font-family: system-ui, sans-serif;
  color-scheme: dark light;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 1rem;
}
        

JS


        const available = document.querySelector('.available');
const notAvailable = document.querySelector('.not-available');
const ul = document.querySelector('ul');
const lastUpdated = document.querySelector('.last-updated');

const updateContent = async () => {
  const data = await fetch(
    'https://worldtimeapi.org/api/timezone/Europe/London.json'
  ).then((response) => response.json());
  return new Date(data.unixtime * 1000);
};

const registerPeriodicBackgroundSync = async (registration) => {
  const status = await navigator.permissions.query({
    name: 'periodic-background-sync',
  });
  if (status.state === 'granted' && 'periodicSync' in registration) {
    try {
      // Register the periodic background sync.
      await registration.periodicSync.register('content-sync', {
        // An interval of one day.
        minInterval: 24 * 60 * 60 * 1000,
      });
      available.hidden = false;
      notAvailable.hidden = true;

      // List registered periodic background sync tags.
      const tags = await registration.periodicSync.getTags();
      if (tags.length) {
        ul.innerHTML = '';
      }
      tags.forEach((tag) => {
        const li = document.createElement('li');
        li.textContent = tag;
        ul.append(li);
      });

      // Update the user interface with the last periodic background sync data.
      const backgroundSyncCache = await caches.open('periodic-background-sync');
      if (backgroundSyncCache) {
        const backgroundSyncResponse =
          backgroundSyncCache.match('/last-updated');
        if (backgroundSyncResponse) {
          lastUpdated.textContent = `${await fetch('/last-updated').then(
            (response) => response.text()
          )} (periodic background-sync)`;
        }
      }

      // Listen for incoming periodic background sync messages.
      navigator.serviceWorker.addEventListener('message', async (event) => {
        if (event.data.tag === 'content-sync') {
          lastUpdated.textContent = `${await updateContent()} (periodic background sync)`;
        }
      });
    } catch (err) {
      console.error(err.name, err.message);
      available.hidden = true;
      notAvailable.hidden = false;
      lastUpdated.textContent = 'Never';
    }
  } else {
    available.hidden = true;
    notAvailable.hidden = false;
    lastUpdated.textContent = `${await updateContent()} (manual)`;
  }
};

if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    const registration = await navigator.serviceWorker.register('./sw.js');
    console.log('Service worker registered for scope', registration.scope);

    await registerPeriodicBackgroundSync(registration);
  });
}