Сервисный работник и клиентское общение. Выборка завершена - PullRequest
0 голосов
/ 07 марта 2020

Я пытаюсь связать работника сервиса с клиентом в двух направлениях. Моя цель - делегировать обработку запроса клиенту и вернуться к работнику службы с правильным ответом. Последовательность шагов следующая:

Сервисный работник:

  1. Сервисный работник получает запрос
  2. Он проверяет, нужно ли его игнорировать или правильно управлять
  3. Создается сообщение с запросом и идентификатором корреляции
  4. Сообщение отправляется клиенту через входящее сообщение
  5. Пара {id: событие} зарегистрирована в хранилище сообщений.

Клиент:

  1. По входящему каналу приходит сообщение с запросом
  2. Сообщение деструктурируется для получения идентификатора корреляции и запроса
  3. Для запроса создается правильный ответ
  4. Идентификатор ответа и корреляции отправляется обратно работнику через исходящий канал

Работник службы:

  1. Ответное сообщение поступает по исходящему каналу
  2. Сообщение деструктурируется для получения идентификатора корреляции и ответа
  3. Ожидающее сообщение для этого идентификатора извлекается из хранилища сообщений
  4. Новый Объект ответа создается для ответа
  5. Объект ответа возвращается через 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
  })
})

При запуске этого кода выдается следующее сообщение об ошибке. См. [1] в коде сервисного работника. Обратите внимание, что весь коммуникационный поток выполняется правильно.

Uncaught DOMException: Failed to execute 'respondWith' on 'FetchEvent': The event handler is already finished.

Если я не пойму неправильно, это означает, что когда работник покидает клиент, чтобы найти клиентскую часть, событие извлечения завершается. Так что мой подход не действителен. У меня вопрос: как может быть реализована такая двусторонняя связь, когда процесс запрос-ответ разделяется по событиям сообщения?

...