将自定义数据传递给服务工作者同步

时间:2017-01-23 01:22:58

标签: service-worker background-sync

我需要发出POST请求并发送一些数据。我正在使用服务工作者sync来处理离线情况。

但是有没有办法将POST数据传递给服务工作者,所以它再次发出相同的请求?

显然当前的解决方案是在一些客户端存储中存储请求,并在客户端获得连接后 - 从存储中获取请求信息然后发送它们。

更优雅的方式?

PS:我想过让服务工作者向应用程序代码发送消息,以便再次执行请求......但不幸的是,它不知道注册服务工作者的确切客户端:(

2 个答案:

答案 0 :(得分:4)

您可以使用fetch-sync

或者我使用postmessage来解决这个问题,我同意indexedDB看起来很麻烦。

  1. 首先,我从html发送消息。

    
    // send message to serviceWorker
    function sync (url, options) {
      navigator.serviceWorker.controller.postMessage({type: 'sync', url, options})
    }
    
  2. 我在serviceworker中收到此消息,然后将其存储起来。

    
    const syncStore = {}
    self.addEventListener('message', event => {
      if(event.data.type === 'sync') {
        // get a unique id to save the data
        const id = uuid()
        syncStore[id] = event.data
        // register a sync and pass the id as tag for it to get the data
        self.registration.sync.register(id)
      }
      console.log(event.data)
    })
    
  3. 在同步事件中,我获取了数据并获取

    
    self.addEventListener('sync', event => {
      // get the data by tag
      const {url, options} = syncStore[event.tag]
      event.waitUntil(fetch(url, options))
    })
    
  4. 它在我的测试中运行良好,你可以在获取后删除内存存储

    更重要的是,您可能希望将结果发回给页面。我将通过postmessage以同样的方式做到这一点。

    1. 因为现在我必须在彼此之间进行通信,我将以这种方式更改功能同步

      
      // use messagechannel to communicate
      sendMessageToSw (msg) {
        return new Promise((resolve, reject) => {
          // Create a Message Channel
          const msg_chan = new MessageChannel()
      
      
      // Handler for recieving message reply from service worker
      msg_chan.port1.onmessage = event => {
        if(event.data.error) {
          reject(event.data.error)
        } else {
          resolve(event.data)
        }
      }
      
      navigator.serviceWorker.controller.postMessage(msg, [msg_chan.port2])
      
      }) } // send message to serviceWorker // you can see that i add a parse argument // this is use to tell the serviceworker how to parse our data function sync (url, options, parse) { return sendMessageToSw({type: 'sync', url, options, parse}) }
    2. 我还必须更改消息事件,以便我可以将端口传递给同步事件

      // Handler for recieving message reply from service worker
      msg_chan.port1.onmessage = event => {
        if(event.data.error) {
          reject(event.data.error)
        } else {
          resolve(event.data)
        }
      }
      
      navigator.serviceWorker.controller.postMessage(msg, [msg_chan.port2])
      
    3. 到目前为止,我们可以处理同步事件

      
      self.addEventListener('message', event => {
        if(isObject(event.data)) {
          if(event.data.type === 'sync') {
            // in this way, you can decide your tag
            const id = event.data.id || uuid()
            // pass the port into the memory stor
            syncStore[id] = Object.assign({port: event.ports[0]}, event.data)
            self.registration.sync.register(id)
          }
        }
      })
      
    4. 最后。你不能使用postmessage直接发送响应。因为它是非法的。所以你需要解析它,比如text,json,blob等。我认为这就够了。

      正如您所提到的,您可能想要打开窗口。 我建议您可以使用serviceworker发送通知。

      
      self.addEventListener('sync', event => {
        const {url, options, port, parse} = syncStore[event.tag] || {}
        // delete the memory
        delete syncStore[event.tag]
        event.waitUntil(fetch(url, options)
          .then(response => {
            // clone response because it will fail to parse if it parse again
            const copy = response.clone()
            if(response.ok) {
              // parse it as you like
              copy[parse]()
              .then(data => {
                // when success postmessage back
                port.postMessage(data)
              })
            } else {
              port.postMessage({error: response.status})
            }
          })
          .catch(error => {
            port.postMessage({error: error.message})
          })
        )
      })
      

      当客户端点击时我们可以打开窗口。

答案 1 :(得分:0)

为了与服务人员交流,我使用了一个技巧:

在获取事件监听器中,我这样输入:

self.addEventListener('fetch', event => {
  if (event.request.url.includes("sw_messages.js")) {
    var zib = "some data";
    event.respondWith(new Response("window.msg=" + JSON.stringify(zib) + ";", {
      headers: {
        'Content-Type': 'application/javascript'
      }
    }));
  }

  return;
});

然后,在主html中,我只需添加:

<script src="sw_messages.js"></script>

在页面加载时,全局变量msg将包含(在此示例中)“一些数据”。