ขอแนะนำการดึงข้อมูลในเบื้องหลัง

ในปี 2015 เราได้เปิดตัวการซิงค์ในเบื้องหลังซึ่งทำให้ Service Worker ให้เลื่อนงานออกไปจนกว่าผู้ใช้จะเชื่อมต่อ ซึ่งหมายความว่าผู้ใช้สามารถพิมพ์ ส่งข้อความ กดส่ง และออกจากเว็บไซต์โดยรับรู้ว่าจะมีการส่งข้อความในตอนนี้หรือเมื่อ มีการเชื่อมต่อ

เป็นฟีเจอร์ที่มีประโยชน์ แต่ต้องทำให้โปรแกรมทำงานของบริการต้องทำงานอยู่ในช่วงระยะเวลาของ ดึงข้อมูล กรณีนี้ไม่ใช่ปัญหาสำหรับงานสั้นๆ อย่างการส่งข้อความ แต่หากงานนั้นใช้เวลา การใช้งานนานเกินไป เบราว์เซอร์จะทำให้โปรแกรมทำงานของบริการไม่ได้ทำงาน มิฉะนั้นอาจเสี่ยงต่อความเป็นส่วนตัวของผู้ใช้ และ แบตเตอรี่

ดังนั้น ในกรณีที่คุณต้องการดาวน์โหลดสิ่งที่อาจต้องใช้เวลานาน เช่น ภาพยนตร์ พอดแคสต์ หรือ ในระดับต่างๆ ของเกม ซึ่งเป็นหน้าที่ของการดึงข้อมูลในเบื้องหลัง

การดึงข้อมูลในเบื้องหลังสามารถใช้ได้โดยค่าเริ่มต้นตั้งแต่ Chrome 74

ด้านล่างนี้เป็นการสาธิตสั้นๆ เพียง 2 นาทีเพื่อแสดงสภาพของสิ่งต่างๆ แบบดั้งเดิม เทียบกับการใช้การดึงข้อมูลในเบื้องหลัง:

ลองใช้การสาธิตด้วยตัวคุณเองและเรียกดูโค้ด

วิธีการทำงาน

การดึงข้อมูลในเบื้องหลังมีลักษณะดังนี้

  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 รับอาร์กิวเมนต์ 3 ตัว ดังนี้

พารามิเตอร์
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 ฉันจะ อธิบายรายละเอียดของเรื่องนั้นในภายหลัง คำสัญญาจะปฏิเสธหากผู้ใช้เลือกไม่รับการดาวน์โหลด หรือ พารามิเตอร์ที่ระบุไม่ถูกต้อง

การส่งคำขอหลายรายการสำหรับการดึงข้อมูลในเบื้องหลังครั้งเดียวช่วยให้คุณรวมสิ่งที่ ให้แก่ผู้ใช้ ตัวอย่างเช่น ภาพยนตร์อาจแบ่งออกเป็น 1, 000 รายการ (โดยทั่วไปจะมี 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>
ล้มเลิกการดึงข้อมูลในเบื้องหลัง

สัญญาที่ส่งกลับมาจะแก้ไขเป็น "จริง" หากล้มเลิกการดึงข้อมูลสำเร็จ

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 ดึงข้อมูลไม่สำเร็จอย่างน้อย 1 รายการ
backgroundfetchabort ดึงข้อมูลไม่สำเร็จอย่างน้อย 1 รายการ

ซึ่งจะเป็นประโยชน์มากในกรณีที่คุณต้องการล้างข้อมูลที่เกี่ยวข้องเท่านั้น

backgroundfetchclick ผู้ใช้คลิก UI ความคืบหน้าในการดาวน์โหลด

ออบเจ็กต์เหตุการณ์มีข้อมูลต่อไปนี้

พร็อพเพอร์ตี้
registration BackgroundFetchRegistration
เมธอด
updateUI({ title, icons }) ช่วยให้คุณเปลี่ยนชื่อ/ไอคอนที่ตั้งค่าไว้ในตอนแรกได้ ขั้นตอนนี้เป็นขั้นตอนที่ไม่บังคับ แต่จะช่วยให้คุณ จะให้บริบทเพิ่มเติมหากจำเป็น คุณสามารถทำเช่นนี้ได้เพียง *ครั้งเดียว* ระหว่าง backgroundfetchsuccess และ backgroundfetchfailure กิจกรรม

การตอบสนองต่อความสำเร็จ/ความล้มเหลว

เราได้เห็นเหตุการณ์ progress แล้ว แต่จะมีประโยชน์ก็ต่อเมื่อผู้ใช้เปิดหน้าเว็บ เว็บไซต์ของคุณ ประโยชน์หลักของการดึงข้อมูลในเบื้องหลังคือสิ่งต่างๆ จะทำงานต่อไปหลังจากที่ผู้ใช้ออกจาก หน้าเว็บ หรือแม้กระทั่งปิดเบราว์เซอร์

หากการดึงข้อมูลในเบื้องหลังเสร็จสมบูรณ์ โปรแกรมทำงานของบริการจะได้รับ backgroundfetchsuccess และ event.registration จะเป็นการลงทะเบียนการดึงข้อมูลในเบื้องหลัง

หลังจากเหตุการณ์นี้ คำขอและการตอบกลับที่ดึงมาจะไม่สามารถเข้าถึงได้อีก ดังนั้นหากต้องการ เก็บไว้ และย้ายไปไว้ที่อื่น เช่น API แคช

เช่นเดียวกับเหตุการณ์ของ Service Worker ส่วนใหญ่ ให้ใช้ event.waitUntil เพื่อให้โปรแกรมทำงานของบริการทราบเมื่อเกิดเหตุการณ์ เสร็จสมบูรณ์

ตัวอย่างเช่น ในโปรแกรมทำงานของบริการ

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 รายการเดียว ซึ่งอาจไม่ได้สำคัญสำหรับคุณ ดังนั้นอาจ แต่ก็ควรที่จะคัดลอกการตอบกลับบางอย่างลงในแคชดังเช่นข้างต้น

แสดงความรู้สึกต่อการคลิก

UI ที่แสดงความคืบหน้าในการดาวน์โหลดและผลลัพธ์เป็นแบบคลิกได้ เหตุการณ์ 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 ในรูปแบบรายงานกลุ่มชุมชนฉบับร่าง