חדש: אחזור רקע

Jake Archibald
Jake Archibald

בשנת 2015 השקנו את הסנכרון ברקע, שמאפשר ל-service worker לדחות את העבודה עד שהמשתמש יקבל חיבור. המשמעות היא שהמשתמש יכול להקליד הודעה, ללחוץ על 'שליחה' ולהשאיר את האתר בידיעה שההודעה תישלח עכשיו, או כשיהיה לו קישוריות.

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

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

האחזור ברקע זמין כברירת מחדל החל מגרסה 74 של Chrome.

הנה הדגמה מהירה של שתי דקות שמראה את המצב הרגיל לעומת השימוש ב'אחזור ברקע':

כדאי לנסות את הדמו בעצמכם ולעיין בקוד.

איך זה עובד

כך פועל אחזור ברקע:

  1. אתם אומרים לדפדפן לבצע קבוצה של אחזור נתונים ברקע.
  2. הדפדפן מאחזר את הפריטים האלה ומציג את ההתקדמות למשתמש.
  3. אחרי שהאחזור יושלם או נכשל, הדפדפן יפתח את Service Worker ויפעיל אירוע כדי לספר לכם מה קרה. כאן אתם מחליטים מה לעשות עם התשובות, אם בכלל.

אם המשתמש סוגר דפים באתר אחרי שלב 1, זה בסדר, ההורדה תמשיך. מכיוון שהאחזור גלוי מאוד וקל לבטל אותו, אין חשש לפרטיות מפני משימה של סנכרון ברקע שנמשכת יותר מדי זמן. מכיוון ש-service worker לא פועל כל הזמן, אין חשש שהוא עלול לנצל לרעה את המערכת, למשל לבצע כריית ביטקוין ברקע.

בפלטפורמות מסוימות (כמו Android), יכול להיות שהדפדפן יסגר אחרי שלב 1, כי הדפדפן יכול להעביר את האחזור למערכת ההפעלה.

אם המשתמש מתחיל את ההורדה במצב אופליין או עובר למצב אופליין במהלך ההורדה, האחזור ברקע יושהה ויחודש מאוחר יותר.

ה-API

זיהוי תכונות

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

if ('BackgroundFetchManager' in self) {
  // This browser supports Background Fetch!
}

התחלת אחזור ברקע

ה-API הראשי תלוי ברישום של קובץ שירות (service worker), לכן חשוב לוודא שרשמת קודם קובץ שירות. לאחר מכן:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
    title: 'Episode 5: Interesting things.',
    icons: [{
      sizes: '300x300',
      src: '/ep-5-icon.png',
      type: 'image/png',
    }],
    downloadTotal: 60 * 1024 * 1024,
  });
});

backgroundFetch.fetch מקבלת שלושה ארגומנטים:

פרמטרים
id string
הוא המזהה הייחודי של אחזור הרקע הזה.

backgroundFetch.fetch יידחה אם המזהה תואם לאחזור רקע קיים.

requests Array<Request|string>
הדברים שרוצים לאחזר. מחרוזות יטופלו ככתובות URL ויהפכו ל-Request דרך new Request(theString).

אפשר לאחזר דברים ממקורות אחרים, כל עוד המשאבים מאפשרים זאת, דרך CORS.

הערה: בשלב הזה, דפדפן Chrome לא תומך בבקשות שדורשות בדיקת קדם של CORS.

options אובייקט שעשוי לכלול את הפרטים הבאים:
options.title string
כותרת שתוצג בדפדפן לצד ההתקדמות.
options.icons Array<IconDefinition>
מערך של אובייקטים עם 'src', 'size' ו-'type'.
options.downloadTotal number
הגודל הכולל של גופי התגובה (לאחר הסרת ה-gzip).

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

אם מספר ההורדות ברקע חורג מהמספר שצוין כאן, הוא יבוטל. זה בסדר גמור אם ההורדה קטנה מ-downloadTotal, אז אם אתם לא בטוחים מה יהיה סכום ההורדות, עדיף לנקוט זהירות.

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

שליחת הרבה בקשות לאחזור אחד ברקע מאפשרת לשלב דברים שהם מבחינה לוגית דבר אחד מבחינת המשתמש. לדוגמה, אפשר לחלק את הסרט לאלפי משאבים (בדרך כלל עם MPEG-DASH) ולהוסיף לו משאבים נוספים כמו תמונות. רמה של משחק יכולה להתפרס על פני משאבים רבים של JavaScript, של תמונות ושל אודיו. אבל למשתמש, זה פשוט 'הסרט' או 'השלב'.

אחזור של אחזור קיים ברקע

אפשר לקבל אחזור קיים ברקע כמו:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});

...על ידי העברת ה-id של אחזור הרקע הרצוי. הפונקציה get מחזירה את הערך undefined אם אין אחזור פעיל ברקע עם המזהה הזה.

אחזור ברקע נחשב 'פעיל' מרגע הרישום שלו, עד שהוא מסתיים בהצלחה, נכשל או מבוטל.

אפשר לקבל רשימה של כל האחזורים הפעילים ברקע באמצעות getIds:

navigator.serviceWorker.ready.then(async (swReg) => {
  const ids = await swReg.backgroundFetch.getIds();
});

הרשמות לאחזור ברקע

ל-BackgroundFetchRegistration (bgFetch בדוגמאות שלמעלה) יש את הפרטים הבאים:

מאפיינים
id string
המזהה של אחזור הרקע.
uploadTotal number
מספר הבייטים שיישלחו לשרת.
uploaded number
מספר הבייטים שנשלחו בהצלחה.
downloadTotal number
הערך שצוין כשנרשמת האחזור ברקע, או אפס.
downloaded number
מספר הבייטים שהתקבלו בהצלחה.

הערך הזה עשוי לרדת. לדוגמה, אם החיבור יתנתק ולא ניתן יהיה להמשיך את ההורדה, הדפדפן יתחיל מחדש את אחזור המשאב הזה מאפס.

result

אחד מהבאים:

  • "" – האחזור ברקע פעיל, ולכן עדיין אין תוצאה.
  • "success" – האחזור ברקע בוצע בהצלחה.
  • "failure" – אחזור הרקע נכשל. הערך הזה מופיע רק כשאחזור הרקע נכשל לחלוטין, כלומר הדפדפן לא יכול לנסות שוב או להמשיך.
failureReason

אחד מהבאים:

  • "" – האחזור ברקע לא נכשל.
  • "aborted" – המשתמש ביטל את האחזור ברקע, או ש-abort() הופעל.
  • "bad-status" – לסטטוס של אחת מהתשובות היה סטטוס לא תקין, למשל 404.
  • "fetch-error" – אחת מהשליפות נכשלה מסיבה אחרת, למשל CORS , MIX, תגובה חלקית לא חוקית או כשל רשת כללי באחזור שלא ניתן לנסות שוב.
  • "quota-exceeded" – הגעתם למכסת האחסון במהלך האחזור ברקע.
  • "download-total-exceeded" – חרגתם מהערך שצוין בשדה downloadTotal.
recordsAvailable boolean
האם יש גישה לבקשות או לתשובות הבסיסיות?

אחרי שהערך הזה יהיה שגוי, לא ניתן יהיה להשתמש ב-match וב-matchAll.

שיטות
abort() מחזירה Promise<boolean>
ביטול האחזור ברקע.

ההבטחה שמוחזרת מופיעה עם ערך True אם האחזור בוטל בהצלחה.

matchAll(request, opts) הפונקציה מחזירה את הערך Promise<Array<BackgroundFetchRecord>>
Get the requests and responses.

הארגומנטים כאן זהים לארגומנטים של API המטמון. קריאה ללא ארגומנטים מחזירה הבטחה לכל הרשומות.

המידע על הסיבות האפשריות מפורט כאן.

match(request, opts) הפונקציה מחזירה את הערך Promise<BackgroundFetchRecord>
כמו למעלה, אבל מתייחסת להתאמה הראשונה.
אירועים
progress הפעלה כשאחד מהפרמטרים uploaded,‏ downloaded,‏ result או failureReason משתנה.

מעקב אחרי ההתקדמות

אפשר לעשות זאת באמצעות האירוע progress. חשוב לזכור ש-downloadTotal הוא הערך שסיפקתם, או 0 אם לא סיפקתם ערך.

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
  console.log(`Download progress: ${percent}%`);
});

קבלת הבקשות והתשובות

bgFetch.match('/ep-5.mp3').then(async (record) => {
  if (!record) {
    console.log('No record found');
    return;
  }

  console.log(`Here's the request`, record.request);
  const response = await record.responseReady;
  console.log(`And here's the response`, response);
});

record הוא BackgroundFetchRecord, והוא נראה כך:

מאפיינים
request Request
הבקשה שסופקה.
responseReady Promise<Response>
התגובה שנשלפה.

התשובה מופיעה אחרי הבטחה כי יכול להיות שהיא עדיין לא התקבלה. אם האחזור נכשל, ה-promise יידחה.

אירועים של קובצי שירות (service worker)

אירועים
backgroundfetchsuccess הכל אוחזר בהצלחה.
backgroundfetchfailure אחד או יותר מהאחזורים נכשל.
backgroundfetchabort אחזור אחד או יותר נכשל.

האפשרות הזו שימושית רק אם רוצים לבצע ניקוי של נתונים קשורים.

backgroundfetchclick המשתמש לחץ על ממשק המשתמש של התקדמות ההורדה.

אובייקטי האירוע כוללים את הדברים הבאים:

מאפיינים
registration BackgroundFetchRegistration
שיטות
updateUI({ title, icons }) מאפשר לשנות את השם או את הסמלים שהגדרתם בהתחלה. הוספת התיאור היא אופציונלית, אבל היא מאפשרת לכם לספק יותר הקשר במקרה הצורך. אפשר לעשות את זה *פעם אחת* רק במהלך אירועי backgroundfetchsuccess ו-backgroundfetchfailure.

תגובה להצלחה או לכשל

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

אם אחזור הרקע יושלם בהצלחה, האירוע backgroundfetchsuccess יישלח ל-service worker, ו-event.registration יהיה הרשמת אחזור הרקע.

אחרי האירוע הזה, לא ניתן לגשת יותר לבקשות ולתשובות שאוחזרו, לכן אם רוצים לשמור אותן, צריך להעביר אותן למקום אחר, כמו cache API.

כמו ברוב האירועים של קובצי שירות (service worker), כדאי להשתמש ב-event.waitUntil כדי שה-Service Worker ידע מתי האירוע הסתיים.

לדוגמה, ב-service worker:

addEventListener('backgroundfetchsuccess', (event) => {
  const bgFetch = event.registration;

  event.waitUntil(async function() {
    // Create/open a cache.
    const cache = await caches.open('downloads');
    // Get all the records.
    const records = await bgFetch.matchAll();
    // Copy each request/response across.
    const promises = records.map(async (record) => {
      const response = await record.responseReady;
      await cache.put(record.request, response);
    });

    // Wait for the copying to complete.
    await Promise.all(promises);

    // Update the progress notification.
    event.updateUI({ title: 'Episode 5 ready to listen!' });
  }());
});

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

תגובה לקליק

ממשק המשתמש שבו מוצגות התקדמות ההורדה והתוצאה. האירוע backgroundfetchclick ב-service worker מאפשר לכם להגיב לכך. כמו למעלה, event.registration תהיה ההרשמה לאחזור ברקע.

הדבר הנפוץ שאפשר לעשות עם האירוע הזה הוא לפתוח חלון:

addEventListener('backgroundfetchclick', (event) => {
  const bgFetch = event.registration;

  if (bgFetch.result === 'success') {
    clients.openWindow('/latest-podcasts');
  } else {
    clients.openWindow('/download-progress');
  }
});

מקורות מידע נוספים

תיקון: גרסה קודמת של המאמר הזה נקראה באופן שגוי 'אחזור ברקע' כ'תקן אינטרנט'. ממשק ה-API לא נמצא כרגע בתהליך יצירת התקן, אבל אפשר למצוא את המפרט ב-WICG כטיוטה של דוח קבוצת קהילה.