我正在尝试以双向方式与客户沟通服务工作者。我的目标是将请求的处理委托给客户端,并以适当的响应返回给服务人员。步骤顺序如下:
服务人员:
- Service Worker 收到请求
- 它检查它是否必须被忽略或正确管理
- 带有请求和相关 ID 的消息被创建
- 消息通过 Incoming 消息发送给客户端
- 对 {id: event} 在消息存储中注册
客户:
- 请求消息来自传入通道
- 消息被解构以获取相关 ID 和请求
- 为请求创建适当的响应
- 响应和相关 ID 通过 Outgoing Channel 发送回工作人员
服务人员:
- 响应消息来自传出通道
- 消息被解构以获取相关 ID 和响应
- 此 ID 的待处理消息是从消息存储中检索的
- 为响应创建一个新的 Response 对象
- 响应对象通过返回
respondWith
这是我的服务人员:
const INSTALL = 'install'
const ACTIVE = 'activate'
const FETCH = 'fetch'
const MESSAGE = 'message'
const INSTALLED = 'Worker Installed'
const ACTIVATED = 'Worker Activated'
const FETCHING = 'Worker fetching'
const ICHANNEL = 'whale-ichannel'
const OCHANNEL = 'whale-ochannel'
const HEADERS = { 'Content-Type' : 'text/javascript' }
const PREFIX = '/whale/'
let toJson = JSON.stringify
let toJs = JSON.parse
let Messages = new Map ()
let idx = 0
let iChannel = new BroadcastChannel (ICHANNEL) // Incoming Channel
let oChannel = new BroadcastChannel (OCHANNEL) // Outgoing Channel
self.addEventListener (INSTALL, function (event) {
console.log (INSTALLED)
self.skipWaiting ()
})
self.addEventListener (ACTIVE, function (event) {
console.log (ACTIVATED)
event.waitUntil (clients.claim ())
oChannel.addEventListener (MESSAGE, function ({ data }) {
let id = data.id
let response = data.response
let message = Messages.get (id)
Messages.delete (id)
message.send (response)
})
})
self.addEventListener (FETCH, async function (event) {
if (isRequest (event)) await doRequest (event)
})
function isRequest (event) {
let { request } = event
let { url } = request
let uri = new URL (url)
let path = uri.pathname
let ok = path.startsWith (PREFIX)
return ok
}
function getRequest (event) {
let { request } = event
let { url } = request
let { referrer } = request
let headers = toJs (toJson (request)) || {}
return {
url,
referrer,
headers
}
}
function getResponse (data) {
let headers = HEADERS
let text = data
let response = new Response (text, { headers })
return response
}
function doRequest (event) {
let request = getRequest (event)
let {id, wait} = getMessage (event)
iChannel.postMessage ({ id, request })
return wait
}
function getMessage (event) {
let signal
let wait = new Promise (function (ok) { signal = ok })
let id = idx++
Messages.set (id, {
send : function (data) {
let response = getResponse (data)
event.respondWith (response) // [1]
signal (data)
}
})
return { id, wait }
}
这是我的客户:
const ICHANNEL = 'whale-ichannel' // Incoming Channel
const OCHANNEL = 'whale-ochannel' // Outgoing Channel
// Register worker ...
let iChannel = new BroadcastChannel (ICHANNEL)
let oChannel = new BroadcastChannel (OCHANNEL)
iChannel.addEventListener (MESSAGE, async function ({ data }) {
let { id } = data
let { request } = data
let response = MyFancyResponse (...)
oChannel.postMessage ({
id,
response
})
})
当我运行此代码时,会发出以下消息错误。请参阅 Service Worker 代码中的 [1]。请注意整个通信流程已正确执行。
Uncaught DOMException: Failed to execute 'respondWith' on 'FetchEvent': The event handler is already finished.
如果我没有误解的话,这意味着当工作人员被放弃去寻找客户端时,获取事件就完成了。所以我的方法是无效的。我的问题是:在请求-响应过程通过消息事件拆分的情况下,如何进行这种双向通信?