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

Jake Archibald
Jake Archibald

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

זו תכונה שימושית, אבל היא דורשת שקובץ השירות (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.

Methods
abort() הפונקציה מחזירה את הערך Promise<boolean>
כדי לבטל את האחזור ברקע.

אם האחזור בוטל בהצלחה, ה-promise המוחזר יתקבל עם הערך true.

matchAll(request, opts) הפונקציה מחזירה את הערך Promise<Array<BackgroundFetchRecord>>
. מקבלים את הבקשות והתשובות.

הארגומנטים כאן זהים לארגומנטים של 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
Methods
updateUI({ title, icons }) מאפשרת לשנות את השם או את הסמלים שהגדרתם בהתחלה. הוספת התיאור היא אופציונלית, אבל היא מאפשרת לכם לספק יותר הקשר במקרה הצורך. אפשר לעשות זאת רק *פעם אחת* במהלך אירועי backgroundfetchsuccess ו-backgroundfetchfailure.

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

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

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

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

כמו ברוב האירועים של שירות העבודה, צריך להשתמש ב-event.waitUntil כדי ששירות העבודה ידע מתי האירוע הושלם.

לדוגמה, ב-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 כטיוטה של דוח קבוצת קהילה.