【问题标题】:Angular 5 with CacheStorage带有 CacheStorage 的 Angular 5
【发布时间】:2018-08-07 10:25:01
【问题描述】:

我试图在 Angular 5 中使用 CacheStorage 的承诺,就像在文档中一样:

let test = caches.open('test');
    test.then((result : Cache) => {
      result.add('/test.png')
    })

但我得到了:

ERROR Error: Uncaught (in promise): TypeError: the given value is not a Promise

我尝试在 ts 文件中从 es5 切换到 es6。但是同样的问题

谢谢

【问题讨论】:

  • 使用mergeMapconcatMap 代替map()
  • 没用。我用一个简单的承诺更新了我的帖子。但同样的问题。
  • value 指向什么?是result 还是test
  • result.add('/test.png') 应该返回一个 Promise,但我得到类型错误。

标签: angular caching promise


【解决方案1】:

我在我的一个项目中遇到了同样的问题。我希望我的应用程序在CacheStorage 中存储一些资产。

我的代码看起来像这样:

window.caches.open(CACHE_NAME)
  .then(cache => cache.addAll(resources))
  .catch(err => console.log('error while caching', err));

我得到了同样的错误:

ERROR Error: Uncaught (in promise): TypeError: the given value is not a Promise

在搜索问题时,我发现 Angular 正在用它自己的 ZoneAwarePromise 替换浏览器的全局 Promise 实现以跟踪承诺。不幸的是,CacheStorage 以某种方式期望原生 Promise 而不是 Angular 的 ZoneAwarePromise 并引发错误。

我试图通过临时存储ZoneAwarePromise 并使用原始Promise 重置它来解决这个问题。事实证明这很棘手,导致我放弃交换实现。

我为此找到了两个合适的解决方法

Service Worker 客户端通信

对于我的第二个(我认为更好)解决方法,我为 client service worker 通信打开一个通道,并让 service worker 进行动态缓存。

  1. 在 Service Worker 中实现 message 事件监听器。

    Service Worker 将监听 message 事件以接收并缓存来自客户端的 url 列表。

    self.addEventListener('message', event => {
      const urls = event.data;
    
      if (!urls) {
        event.waitUntil(Promise.reject('nothing to cache'));
        event.ports[0].postMessage(false);
      }
      else {
        event.waitUntil(
          caches.open(version)
            .then(cache => cache.addAll(urls))
            .then(self.skipWaiting())
            .then(() => console.log('dynamic urls cached'))
        );
        event.ports[0].postMessage(true);
      }
    });
    
  2. 为客户端的新 url 添加缓存。

    这会向 Service Worker 提交一条消息,其中包含 url 列表作为有效负载。

    function addToCache(urls: string[]): Promise<any> {
      return new Promise((resolve, reject) => {
        let messageChannel = new MessageChannel();
        messageChannel.port1.onmessage = function(event) {
          if (event.data.error) reject(event.data.error);
          else resolve(event.data);
        };
    
        if (!navigator.serviceWorker.controller) reject('no service worker contoller found')
        else navigator.serviceWorker.controller.postMessage(urls, [messageChannel.port2]);
      });
    }
    
  3. 确保 Service Worker 处于活动状态以进行缓存。

    如果有新的 service worker 控制,我会在客户端使用 controllerchange 事件来触发重新缓存。 (见:service worker lifecycle documentation)。

    const resources = ['some/url/to/cache.png', 'another/url/to/cache.css']
    
    navigator.serviceWorker.addEventListener('controllerchange', () => 
      addToCache(resources)
        .then(data => console.log('urls cached', data))
        .catch(err => console.log('error while caching', err))
    );
    

隐形iframe

我针对这个问题的第一个解决方法实际上只是一个 hack,但它现在可以工作。

const hiddenFrame = document.createElement('iframe');
hiddenFrame.style.display = 'none';
document.documentElement.appendChild(hiddenFrame);

hiddenFrame.contentWindow.caches.open(CACHE_NAME)
  .then(cache => cache.addAll(resources))
  .then(() => document.documentElement.removeChild(hiddenFrame))
  .catch(err => console.log('error while caching', err));

我在这里做什么:

我创建了一个隐藏的iframe 并将其附加到我的文档中。在 iframe 内部,Angular 没有运行,所以存在原生的 Promise 实现。然后我从 iframe 的 contentWindow 对象访问缓存。成功缓存后,我删除了iframe

我仍在寻找更好的解决方案。

我的下一个尝试是向 service worker 发送消息并让它触发资源的缓存。 Service Worker 也没有 Angular 运行。

【讨论】:

    【解决方案2】:

    这很容易错过 -

    1) CacheStorage API(或 Cache API)仅在 localhost 和 HTTP/2 上可用。
    2) 出于纯粹主义者的原因,更喜欢使用window.caches.open(CACHE_NAME),因为cacheswindow 对象中存在的单例实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-07
      • 2018-07-09
      • 2018-10-19
      相关资源
      最近更新 更多