后台运行 Fetch API 实战

1,669 阅读4分钟

Service Worker API 正在不断扩展,用以容纳越来越多的后台运行程序,我曾经写过关于 推送通知(Push Notifications)与后台同步(Background Sync),最近我探索出了如何在后台运行 Fetch API,下面就给大家介绍下具体情况。

原文地址:philna.sh/blog/2017/0…

发表于 2017年7月4日由菲尔·纳什

下载和上传

后台运行 fetch API 可以解决以下两个问题:

  1. 当 Service Worker 正在下载大型文件,用户离开浏览器,即使使用event.waitUntil, Service Worker 的进程依然会被杀死

  2. 当上传文件的时候,用户离开浏览器,上传也会被中断,导致上传失败。

后台运行 fetch API 允许开发人员离开当前的页面,进行加载和操作大文件或文件列表。这样可以加大上传和下载的成功率,并允许sevice worker 缓存结果。

在写这篇文章的时候,这个功能已经支持测试了。 可以打开在chrome 中打开 chrome://flags 进行设置。

举一个例子

我决定构建一个简单的概念来证明 API 的工作原理。案例的在线运行地址,或者你可以查看并运行GitHub的源代码。我不想随便加载一个大文件,所以这个例子试图加载一个10秒延迟的图像(在Glitch上4.5秒)。

如果图片有缓存,service worker 为直接从缓存提供图像,否则发起网络请求。

// sw.js
self.addEventListener('fetch', function(event) {
  if (event.request.url.match(/images/)) {
    event.respondWith(
      caches.open('downloads').then(cache => {
        return cache.match(event.request).then(response => {
          return response || fetch(event.request);
        });
      })
    );
  } else {
    event.respondWith(caches.match(event.request));
  }
});

标准的 Service Worker 只是到这来为止,接下来我们介绍点儿新的东西。

点击“开始下载”按钮将启动后台读取。读取被标记为“大文件”的东西(我是这样想像的)。当后台下载正在进行时,您可以离开页面,下载将在后台继续工作。

// index.html
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(reg) {
    const button = document.getElementById('download');
    if ('backgroundFetch' in reg) {
      button.addEventListener('click', function(event) {
        reg.backgroundFetch.fetch('large-file', [new Request('/images/twilio.png')], {
          title: 'Downloading large image'
        }).then(function(backgroundFetch) { console.log(backgroundFetch) });
      })
    }
  });
}

当下载完成时,Service Worker 会触发 backgroundfetched 事件。该事件有一个 fetches 属性的数组对象,指向包含每个请求和响应。循环 fetches(即使只有一个, 代码也要循环一下),将响应放入缓存。

self.addEventListener('backgroundfetched', function(event) {
  event.waitUntil(
    caches.open('downloads').then(function(cache) {
      event.updateUI('Large file downloaded');
      registration.showNotification('File downloaded!');
      const promises = event.fetches.map(({ request, response }) => {
        if (response && response.ok) {
          return cache.put(request, response.clone());
        }
      });
      return Promise.all(promises);
    })
  );
});

现在,当请求图像时,它将直接从 Service Worker 缓存中获取,不再需要下载,加载变得很快。

后台运行 API 的想法

您可以看到,实现后台运行 API 一点儿也不难。你可能会发现,我在初次尝试的时候,没有写 backgroundfetchfail,backgroundfetchabort和backgroundfetchclick事件。但对于一个完整功能齐全的应用程序,您需要完善这些功能。

音频难题

最开始的时候,我用音频做为例子,但是音频很难缓存成功。但这个也不是后台运行 API 的问题,而是 Service Worker 对于获取请求的问题。对于富Web应用来说,如何更简单的下载过大的音频和视频,是所有人对于 Service Worker 都应该讨论的问题。

弹出下载

当在桌面上的Chrome中进行测试时,我惊讶地发现该文件在以用户可见的方式下载。我期望的是该文件将留给 Service Worker 去处理和缓存。而不是出现在我的下载文件夹。例如,如果您正在使用后台运行API来获取游戏资源,我不希望用在应用程序中使用的资源却丢到用户的下载文件夹下。但当我在Android设备上测试时,并没有发生这种情况。

UI 如何?

在API中有一些影响浏览器UI的地方,您可以使用 event.updateUI 方法设置一个标题和图标,以获取并更新标题。但是,在桌面或手机测试中,我无法看到这个UI。我只能假设它在工作。

对未来的展望

我很喜欢研究这些早期的API,他们有巨大的潜力(我很高兴返回 Web Share API!)。后台运行 API 对于需要下载大文件或大量文件的应用程序将是非常好的,比仅仅使用 servive worker 进行缓存要好得多。对于视频和音频应用、游戏和虚拟现实体验也肯定会受益匪浅。

我认为浏览器在为开发人员和用户提供一个很好的体验方面还有很多需要完善的地方,看到 Service Worker 一点一滴的进步是非常兴奋的。我的下一个计划是构建一个充分利用后台运行API以及Service Worker 其他特征,解决实际问题的应用程序。欢迎关注我的GitHub关注进度。

如果您对后台运行API感兴趣,则有关GitHub提案的更多示例和讨论。如果您对浏览器的未来可支持的功能感兴趣,请在Twitter上给我留言。

如果你喜欢这篇文章,请分享