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

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

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

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

Background Fetch พร้อมใช้งานโดยค่าเริ่มต้นตั้งแต่ Chrome 74

นี่คือการสาธิตสั้นๆ 2 นาทีที่แสดงสถานะแบบเดิมๆ เทียบกับการใช้การดึงข้อมูลในเบื้องหลัง

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

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

  1. คุณบอกเบราว์เซอร์ให้ทำการดึงข้อมูลกลุ่มหนึ่งในเบื้องหลัง
  2. เบราว์เซอร์จะดึงข้อมูลเหล่านั้นและแสดงความคืบหน้าต่อผู้ใช้
  3. เมื่อการดึงข้อมูลเสร็จสมบูรณ์หรือล้มเหลว เบราว์เซอร์จะเปิด Service Worker และทริกเกอร์เหตุการณ์ เพื่อแจ้งให้คุณทราบสิ่งที่เกิดขึ้น ในส่วนนี้ คุณจะตัดสินใจได้ว่าจะทำอย่างไรกับคำตอบ (หากมี)

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

ในบางแพลตฟอร์ม (เช่น 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 รับอาร์กิวเมนต์ 3 รายการ ได้แก่

พารามิเตอร์
id string
ระบุการดึงข้อมูลในเบื้องหลังนี้อย่างไม่ซ้ำกัน

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

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

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

matchAll(request, opts) การคืนสินค้า Promise<Array<BackgroundFetchRecord>>
รับคำขอ และการตอบกลับ

อาร์กิวเมนต์ในที่นี้เหมือนกับ API ของแคช การเรียกใช้โดยไม่มีอาร์กิวเมนต์จะแสดงผล Promise สำหรับระเบียนทั้งหมด

ดูรายละเอียดเพิ่มเติมด้านล่าง

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

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

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

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

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

การตอบสนองต่อการคลิก

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');
  }
});

แหล่งข้อมูลเพิ่มเติม

การแก้ไข: บทความเวอร์ชันก่อนหน้านี้อ้างถึง Background Fetch ว่าเป็น "มาตรฐานเว็บ" อย่างไม่ถูกต้อง ขณะนี้ API ยังไม่ได้อยู่ในเส้นทางมาตรฐาน และข้อกำหนดอยู่ใน WICG ในรูปแบบรายงานฉบับร่างของกลุ่มชุมชน