Giới thiệu về Tìm nạp trong nền

Jake Archibald
Jake Archibald

Vào năm 2015, chúng tôi giới thiệu tính năng Đồng bộ hoá trong nền, cho phép service worker để trì hoãn công việc cho đến khi người dùng có kết nối. Điều này có nghĩa là người dùng có thể nhập tin nhắn, nhấn gửi và rời khỏi trang web khi biết rằng tin nhắn sẽ được gửi ngay bây giờ hoặc khi nào có kết nối.

Đây là một tính năng hữu ích, nhưng nó đòi hỏi trình chạy dịch vụ phải tồn tại trong suốt thời gian tìm nạp. Đó không phải là vấn đề đối với các công việc ngắn như gửi tin nhắn, nhưng nếu tác vụ mất nhiều thời gian quá lâu thì trình duyệt sẽ tắt worker dịch vụ, nếu không sẽ gây rủi ro cho quyền riêng tư và pin.

Vậy nếu bạn cần tải nội dung nào đó có thể mất nhiều thời gian, chẳng hạn như phim, podcast hoặc của một trò chơi. Đó chính là mục đích của tính năng Tìm nạp trong nền.

Tính năng Tìm nạp trong nền được cung cấp theo mặc định kể từ Chrome 74.

Dưới đây là bản minh hoạ nhanh dài 2 phút cho thấy trạng thái truyền thống so với cách sử dụng Tìm nạp trong nền:

Tự dùng thử bản minh hoạduyệt xem mã.

Cách hoạt động

Thao tác tìm nạp ở chế độ nền hoạt động như sau:

  1. Bạn yêu cầu trình duyệt thực hiện một nhóm các lần tìm nạp trong nền.
  2. Trình duyệt sẽ tìm nạp những thông tin đó và hiển thị tiến trình cho người dùng.
  3. Sau khi tìm nạp xong hoặc không thành công, trình duyệt sẽ mở trình chạy dịch vụ của bạn và kích hoạt một sự kiện để cho bạn biết những gì đã xảy ra. Đây là nơi bạn quyết định sẽ làm gì với các câu trả lời, nếu có.

Nếu người dùng đóng các trang trong trang web của bạn sau bước 1 thì quá trình tải xuống sẽ tiếp tục. Bởi vì quá trình tìm nạp có thể nhìn thấy rõ ràng và dễ dàng bị huỷ bỏ, thời gian quá dài cũng không phải là lo ngại về quyền riêng tư tác vụ đồng bộ hoá ở chế độ nền. Do trình chạy dịch vụ không liên tục chạy nên không phải lo lắng nó có thể lạm dụng hệ thống, chẳng hạn như đào bitcoin trong nền.

Trên một số nền tảng (chẳng hạn như Android), trình duyệt có thể đóng sau bước 1, vì trình duyệt có thể chuyển việc tìm nạp sang hệ điều hành.

Nếu người dùng bắt đầu tải xuống khi không có mạng hoặc không có kết nối mạng trong khi tải xuống, thì chế độ nền tìm nạp sẽ bị tạm dừng và tiếp tục sau đó.

API

Phát hiện tính năng

Giống như bất kỳ tính năng mới nào, bạn đều muốn biết trình duyệt có hỗ trợ tính năng đó hay không. Đối với Tìm nạp trong nền, đó là đơn giản như:

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

Bắt đầu tìm nạp ở chế độ nền

API chính sẽ ngừng đăng ký service worker, vì vậy, hãy đảm bảo bạn đã đăng ký service worker trước. Sau đó:

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 nhận 3 đối số:

Thông số
id string
xác định duy nhất lần tìm nạp trong nền này.

backgroundFetch.fetch sẽ từ chối nếu giấy tờ tuỳ thân khớp với nền hiện có tìm nạp.

requests Array<Request|string>
Nội dung cần tìm nạp. Chuỗi sẽ được coi là URL và được chuyển thành Request qua new Request(theString).

Bạn có thể tìm nạp thông tin từ các nguồn khác, miễn là các tài nguyên đó cho phép việc này qua CORS.

Lưu ý: Chrome hiện không hỗ trợ các yêu cầu cần có quy trình kiểm tra CORS.

options Một đối tượng có thể bao gồm:
options.title string
Tiêu đề cho trình duyệt hiển thị cùng với tiến trình.
options.icons Array<IconDefinition>
Một mảng đối tượng có "src", "size" và "type".
options.downloadTotal number
Tổng kích thước của nội dung phản hồi (sau khi được giải nén).

Mặc dù không bắt buộc nhưng bạn nên cung cấp thông tin này. Thông tin này được dùng để cho biết cho người dùng về kích thước của tệp tải xuống cũng như để cung cấp thông tin về tiến trình. Nếu bạn không cung cấp điều này, trình duyệt sẽ cho người dùng biết kích thước không xác định và do đó người dùng có thể có thể huỷ quá trình tải xuống.

Nếu số lượt tải xuống tìm nạp ở chế độ nền vượt quá số lượng được cung cấp ở đây, thì lượt tải xuống này sẽ bị huỷ. Bây giờ hoàn toàn không có vấn đề gì nếu tệp tải xuống nhỏ hơn downloadTotal, vì vậy nếu bạn không để chắc chắn tổng số lượt tải xuống là bao nhiêu, thì tốt nhất là nên thận trọng.

backgroundFetch.fetch trả về lời hứa phân giải bằng BackgroundFetchRegistration. Tôi sẽ tôi sẽ trình bày chi tiết về vấn đề đó ở phần sau. Lời hứa sẽ từ chối nếu người dùng đã chọn không tải xuống hoặc trong số các thông số đã cho không hợp lệ.

Việc cung cấp nhiều yêu cầu cho một lần tìm nạp ở chế độ nền cho phép bạn kết hợp mọi thứ theo logic đối với người dùng. Ví dụ: một bộ phim có thể được chia thành 1.000 tài nguyên (thông thường với MPEG-DASH), và đi kèm với các tài nguyên bổ sung như hình ảnh. Một cấp độ của trò chơi có thể được trải rộng trên nhiều Tài nguyên JavaScript, hình ảnh và âm thanh. Nhưng với người dùng, đó chỉ là "bộ phim", hay "cấp độ".

Lấy thao tác tìm nạp ở chế độ nền hiện có

Bạn có thể sử dụng phương thức tìm nạp trong nền hiện có như sau:

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

...bằng cách truyền id (mã nhận dạng) của lần tìm nạp dữ liệu nền mà bạn muốn. get sẽ trả về undefined nếu không có đang hoạt động tìm nạp nền bằng ID đó.

Quá trình tìm nạp ở chế độ nền được coi là "đang hoạt động" từ thời điểm đăng ký cho đến khi thành công, không thành công hoặc bị huỷ bỏ.

Bạn có thể nhận danh sách tất cả các lần tìm nạp ở chế độ nền đang hoạt động bằng cách sử dụng getIds:

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

Lượt đăng ký tìm nạp ở chế độ nền

BackgroundFetchRegistration (bgFetch trong các ví dụ trên) có:

Thuộc tính
id string
Mã nhận dạng của lần tìm nạp ở chế độ nền.
uploadTotal number
Số byte sẽ được gửi đến máy chủ.
uploaded number
Số byte đã gửi thành công.
downloadTotal number
Giá trị được cung cấp khi tìm nạp ở chế độ nền được đăng ký, hoặc 0.
downloaded number
Số byte đã nhận thành công.

Giá trị này có thể giảm. Ví dụ: nếu kết nối bị mất và không thể tải xuống được tiếp tục, trong trường hợp này trình duyệt sẽ khởi động lại quá trình tìm nạp cho tài nguyên đó từ đầu.

result

Một trong số sau:

  • "" – Tính năng tìm nạp trong nền đang hoạt động nên chưa có kết quả nào.
  • "success" – Tìm nạp ở chế độ nền thành công.
  • "failure" - Tìm nạp trong nền không thành công. Giá trị này chỉ xuất hiện khi tìm nạp trong nền hoàn toàn không thành công, vì trong trình duyệt không thể thử lại/tiếp tục.
failureReason

Một trong số sau:

  • "" - Quá trình tìm nạp ở chế độ nền đã không thành công.
  • "aborted" – Người dùng đã huỷ quá trình tìm nạp ở chế độ nền hoặc Đã gọi abort().
  • "bad-status" – Một trong các phản hồi có trạng thái không tốt, ví dụ: 404.
  • "fetch-error" – Một trong các lần tìm nạp không thành công vì một số lý do khác, ví dụ: CORS, MIX, phản hồi một phần không hợp lệ hoặc lỗi mạng chung để tìm nạp Không thể thử lại.
  • "quota-exceeded" - Đã đạt đến hạn mức bộ nhớ khi ở chế độ nền tìm nạp.
  • "download-total-exceeded" – `downloadTotal` được cung cấp là vượt quá.
recordsAvailable boolean
Có thể truy cập vào các yêu cầu/phản hồi cơ bản không?

Khi giá trị này là false, thì bạn không thể dùng matchmatchAll.

Phương thức
abort() Trả về Promise<boolean>
Huỷ thao tác tìm nạp ở chế độ nền.

Lời hứa được trả về sẽ phân giải bằng giá trị true nếu quá trình tìm nạp bị huỷ thành công.

matchAll(request, opts) Trả về Promise<Array<BackgroundFetchRecord>>
Nhận yêu cầu và phản hồi.

Các đối số ở đây giống như bộ nhớ đệm . Việc gọi mà không có đối số sẽ trả về hứa hẹn cho tất cả bản ghi.

Hãy xem mục bên dưới để biết thêm thông tin.

match(request, opts) Trả về Promise<BackgroundFetchRecord>
Như trên, nhưng phân giải bằng trận đấu đầu tiên.
Sự kiện
progress Được kích hoạt khi có bất kỳ giá trị nào trong số uploaded, downloaded, result hoặc Đã thay đổi failureReason.

Đang theo dõi tiến trình

Bạn có thể thực hiện việc này thông qua sự kiện progress. Hãy nhớ rằng downloadTotal là bất kỳ giá trị nào mà bạn đã cung cấp hoặc 0 nếu bạn không cung cấp giá trị.

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}%`);
});

Nhận yêu cầu và phản hồi

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 là một BackgroundFetchRecord và có dạng như sau:

Thuộc tính
request Request
Yêu cầu được đưa ra.
responseReady Promise<Response>
Phản hồi được tìm nạp.

Câu trả lời được thực hiện dựa trên một lời hứa vì có thể chưa nhận được. Lời hứa sẽ từ chối nếu tìm nạp không thành công.

Sự kiện của trình chạy dịch vụ

Sự kiện
backgroundfetchsuccess Đã tìm nạp xong mọi thứ.
backgroundfetchfailure Một hoặc nhiều lần tìm nạp không thành công.
backgroundfetchabort Một hoặc nhiều lần tìm nạp không thành công.

Điều này chỉ thực sự hữu ích nếu bạn muốn xoá các dữ liệu có liên quan.

backgroundfetchclick Người dùng nhấp vào giao diện người dùng về tiến trình tải xuống.

Các đối tượng sự kiện có như sau:

Thuộc tính
registration BackgroundFetchRegistration
Phương thức
updateUI({ title, icons }) Cho phép bạn thay đổi tiêu đề/biểu tượng bạn đặt ban đầu. Đây là thao tác không bắt buộc, nhưng cho phép bạn cung cấp thêm ngữ cảnh nếu cần. Bạn chỉ có thể thực hiện việc này *một lần* trong thời gian backgroundfetchsuccessbackgroundfetchfailure sự kiện.

Phản ứng với thành công/thất bại

Chúng ta đã thấy sự kiện progress, nhưng sự kiện này chỉ hữu ích khi người dùng có một trang đang mở trang web của bạn. Lợi ích chính của tính năng tìm nạp trong nền là mọi thứ tiếp tục hoạt động sau khi người dùng rời khỏi hoặc thậm chí đóng trình duyệt.

Nếu quá trình tìm nạp trong nền hoàn tất thành công, trình chạy dịch vụ của bạn sẽ nhận được Sự kiện backgroundfetchsuccessevent.registration sẽ là hoạt động đăng ký tìm nạp ở chế độ nền.

Sau sự kiện này, bạn sẽ không thể truy cập vào các yêu cầu và phản hồi đã tìm nạp được nữa. Vì vậy, nếu muốn giữ lại chúng, hãy di chuyển chúng đến một nơi nào đó như API bộ nhớ đệm.

Tương tự như với hầu hết các sự kiện của trình chạy dịch vụ, hãy sử dụng event.waitUntil để trình chạy dịch vụ biết thời điểm xảy ra sự kiện đã hoàn tất.

Ví dụ như trong trình chạy dịch vụ:

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

Lỗi có thể là lỗi 404. Lỗi này có thể không quan trọng với bạn, vì vậy có thể bạn vẫn nên sao chép một số phản hồi vào bộ nhớ đệm như trên.

Thể hiện cảm xúc khi nhấp vào

Giao diện người dùng hiển thị tiến trình và kết quả tải xuống có thể nhấp vào. Sự kiện backgroundfetchclick ở service worker cho phép bạn phản ứng với điều này. Như trên, event.registration sẽ là nền đăng ký tìm nạp.

Việc phổ biến cần làm với sự kiện này là mở một cửa sổ:

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

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

Tài nguyên khác

Đính chính: Phiên bản trước của bài viết này đã nhầm lẫn về việc Tìm nạp trong nền là một "tiêu chuẩn web". API này hiện không thuộc kênh tiêu chuẩn. Bạn có thể xem quy cách trên WICG dưới dạng Báo cáo dự thảo của nhóm cộng đồng.