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 đã ra mắt tính năng Đồng bộ hoá ở chế độ nền, cho phép worker dịch vụ 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 thông báo, nhấn nút gửi và rời khỏi trang web mà biết rằng thông báo sẽ được gửi ngay hoặc khi họ có kết nối.

Đây là một tính năng hữu ích nhưng đòi hỏi trình chạy dịch vụ phải hoạt động trong suốt thời gian tìm nạp. Điều đó không gây ra 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 quá nhiều thời gian, trình duyệt sẽ tắt worker dịch vụ, nếu không thì điều này sẽ gây rủi ro cho quyền riêng tư và pin của người dùng.

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ác cấp độ của trò chơi. Đó chính là mục đích của tính năng Tìm nạp ở chế độ nền.

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

Sau đây là bản minh hoạ nhanh trong 2 phút cho thấy trạng thái truyền thống so với khi sử dụng tính năng Tìm nạp ở chế độ nền:

Tự thử bản minh hoạduyệt qua mã.

Cách hoạt động

Quá trình 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 lệnh tìm nạp ở chế độ 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 điều gì đã xảy ra. Đây là nơi bạn quyết định sẽ làm gì với câu trả lời, nếu có.

Nếu người dùng đóng các trang trên trang web của bạn sau bước 1, thì quá trình tải xuống sẽ tiếp tục. Vì quá trình tìm nạp rất dễ thấy và dễ huỷ, nên không có vấn đề về quyền riêng tư khi một tác vụ đồng bộ hoá ở chế độ nền diễn ra quá lâu. Vì worker dịch vụ không chạy liên tục, nên bạn không cần lo lắng rằng worker dịch vụ có thể lạm dụng hệ thống, chẳng hạn như khai thác bitcoin ở chế độ 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 hoạt động 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ó mạng trong khi tải xuống, thì quá trình tìm nạp trong nền sẽ bị tạm dừng và tiếp tục sau.

API

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

Giống như mọi tính năng mới, bạn muốn phát hiện xem trình duyệt có hỗ trợ tính năng đó hay không. Đối với tính năng Tìm nạp ở chế độ nền, bạn chỉ cần làm như sau:

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

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

API chính treo trên một lượt đăng ký worker dịch vụ, vì vậy, trước tiên, hãy đảm bảo bạn đã đăng ký một worker dịch vụ. 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 riêng biệt lượt tìm nạp ở chế độ nền này.

backgroundFetch.fetch sẽ từ chối nếu mã nhận dạng khớp với một lệnh tìm nạp ở chế độ nền hiện có.

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

Bạn có thể tìm nạp nội dung từ các nguồn gốc khác miễn là tài nguyên cho phép thông qua CORS.

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

options Một đối tượng có thể bao gồm:
options.title string
Tiêu đề để trình duyệt hiển thị cùng với tiến trình.
options.icons Array<IconDefinition>
Một mảng các đố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 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. Tệp này dùng để cho người dùng biết kích thước tệp tải xuống và cung cấp thông tin về tiến trình. Nếu bạn không cung cấp thông tin này, trình duyệt sẽ cho người dùng biết kích thước không xác định, do đó, người dùng có thể sẽ huỷ 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ì tác vụ này sẽ bị huỷ. 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 biết chắc tổng số lượt tải xuống là bao nhiêu, thì tốt nhất là bạn nên thận trọng.

backgroundFetch.fetch trả về một lời hứa phân giải bằng BackgroundFetchRegistration. Tôi sẽ đề cập chi tiết về vấ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 một trong các tham số được cung cấp 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 những thứ về mặt logic là một thứ duy nhất với người dùng. Ví dụ: một bộ phim có thể được chia thành hàng nghìn tài nguyên (thường là 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 phân bổ trên nhiều tài nguyên JavaScript, hình ảnh và âm thanh. Nhưng đối với người dùng, đó chỉ là "phim" hoặc "cấp độ".

Tìm nạp một yêu cầu tìm nạp ở chế độ nền hiện có

Bạn có thể lấy một lệnh 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 của lệnh tìm nạp ở chế độ nền mà bạn muốn. get trả về undefined nếu không có hoạt động tìm nạp trong nền nào có mã nhận dạng đó.

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

Bạn có thể lấy danh sách tất cả các lượt tìm nạp ở chế độ nền đang hoạt động bằ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ó các thuộc tính sau:

Thuộc tính
id string
Mã nhận dạng của lệnh tìm nạp ở chế độ nền.
uploadTotal number
Số byte cần 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 bằng 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ị ngắt và không thể tiếp tục tải xuống, thì trình duyệt sẽ bắt đầu lại quá trình tìm nạp tài nguyên đó từ đầu.

result

Một trong số sau:

  • "" – Tính năng tìm nạp ở chế độ nền đang hoạt động nên chưa có kết quả.
  • "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 quá trình tìm nạp ở chế độ nền hoàn toàn không thành công, vì 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 gặp lỗi.
  • "aborted" – Người dùng đã huỷ quá trình tìm nạp ở chế độ nền hoặc abort() đã được gọi.
  • "bad-status" – Một trong các phản hồi có trạng thái không ổn, 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 đối với một lần tìm nạp không thể thử lại.
  • "quota-exceeded" – Đã đạt hạn mức bộ nhớ trong quá trình tìm nạp ở chế độ nền.
  • "download-total-exceeded" – Bạn đã vượt quá giới hạn `downloadTotal` đã cung cấp.
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à sai, bạn không thể sử dụng matchmatchAll.

Phương thức
abort() Trả về Promise<boolean>
Huỷ tìm nạp trong nền.

Lời hứa được trả về sẽ phân giải bằng 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 các yêu cầu và phản hồi.

Các đối số ở đây giống với API 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>
Giống như trên, nhưng phân giải bằng kết quả khớp đầu tiên.
Sự kiện
progress Được kích hoạt khi bất kỳ uploaded, downloaded, result hoặc failureReason nào thay đổi.

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 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 cung cấp.
responseReady Promise<Response>
Phản hồi được tìm nạp.

Phản hồi nằm sau một lời hứa vì có thể bạn chưa nhận được phản hồi đó. Lời hứa sẽ từ chối nếu không tìm nạp được.

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

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

Đ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ó:

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 mà bạn đã đặt ban đầu. Đây là phần 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 các sự kiện backgroundfetchsuccessbackgroundfetchfailure.

Phản ứng với thành công/không thành công

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 mở một trang trên 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 trang hoặc thậm chí là đóng trình duyệt.

Nếu quá trình tìm nạp ở chế độ nền hoàn tất thành công, worker dịch vụ sẽ nhận được sự kiện backgroundfetchsuccessevent.registration sẽ là lượt đă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 bạn muốn giữ lại chúng, hãy di chuyển chúng đến một nơi nào đó, chẳng hạn như API Bộ nhớ đệm.

Giống như hầu hết các sự kiện của worker dịch vụ, hãy sử dụng event.waitUntil để worker dịch vụ biết thời điểm sự kiện hoàn tất.

Ví dụ: trong worker 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à do một lỗi 404 duy nhất, điều này có thể không quan trọng đối với bạn. Vì vậy, bạn vẫn nên sao chép một số phản hồi vào bộ nhớ đệm như trên.

Phản hồi lượt nhấp

Bạn có thể 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. Sự kiện backgroundfetchclick trong trình chạy dịch vụ cho phép bạn phản ứng với điều này. Như trên, event.registration sẽ là đăng ký tìm nạp ở chế độ nền.

Việc thường 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

Chỉnh sửa: Phiên bản trước của bài viết này đã gọi nhầm tính năng Tìm nạp ở chế độ nền là "tiêu chuẩn web". API này hiện không nằm trong lộ trình tiêu chuẩn, bạn có thể tìm thấy thông số kỹ thuật trong WICG dưới dạng Báo cáo nhóm cộng đồng nháp.