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

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, אז ודאו קודם שרשמתם קובץ שירות (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
האם אפשר לגשת לבקשות או לתגובות הבסיסיות?

אם המדיניות הזו מוגדרת כ-False, לא ניתן להשתמש ב-match וב-matchAll.

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

ההבטחה שמוחזרת מופיעה עם ערך 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>
התגובה שאוחזרה.

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

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

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

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

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

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

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

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

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

אם האחזור ברקע יושלם, ה-Service Worker יקבל את האירוע backgroundfetchsuccess ו-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 כטיוטה של דוח של קבוצה קהילתית.