Worker 使用同步 XMLHttpRequest 从 GUI 获取数据

Worker using synchronous XMLHttpRequest to get data from GUI

我想要一个位于调用堆栈深处的 Web Worker,以便能够发出同步请求以从 GUI 获取信息。

GUI 本身没有被阻止——它能够处理消息。但是worker的栈上的JavaScript并不是写成async / await的样式。它只是很多同步代码。因此,如果 GUI 尝试使用 postMessage 将响应发送回 worker,那只会卡在 onmessage() 队列中。

我发现至少一种适用于当今浏览器的 hack。 工作人员可以将消息发送到 GUI 以获得它想要的信息——连同某种 ID (例如 UUID)。然后它可以向具有该 ID 的 Web 上的某个服务器发出同步 XMLHttpRequest--

当工作人员等待该 http 请求时,GUI 会处理信息请求。完成后,它会向具有 ID 和数据的同一服务器执行 XMLHttpRequest to POST。服务器然后使用该信息来完成它为工作人员打开的阻塞请求。这样就完成了同步请求。

将 GUI 和 worker 之间的同步外包给服务器似乎很轻率。但如果必须的话,我会这样做,因为 它不适合强制以异步方式编写工作代码的用例。 另外,我假设 总有一天 浏览器将能够在本机进行这种同步。不过好像可以使用的一种机制--SharedArrayBuffer,暂时被禁用了

UPDATE circa late 2018: SharedArrayBuffer was re-enabled in Chrome for desktop v67. It's not back on for Android Chrome or other browsers yet, and might be a while.

(像 compiling a JavaScript interpreter into the worker 这样 JS 堆栈可以随意暂停和重新启动的更奇怪的选项不在 table 上——不仅仅是因为大小和性能,但无法使用浏览器的开发人员工具调试 worker。)

所以...

我也想知道是否有某种现成的 JS 就绪服务已经实现了回显服务器的协议...如果有人知道的话。好像还挺容易写的。

嗯......也许你可以像这样即时创建你的工人

function startNewWorker (code) {
  var blob = new Blob([code], {type: "application/javascript"});
  var worker = new Worker(URL.createObjectURL(blob));
}

然后,对于您需要的每个新 http 请求,您启动自己的 worker:

const w1 = startNewWorker(yourCodeThatDoesSomething);
w1.onmessage = function () { /* ... */};

const w2 = startNewWorker(yourCodeThatDoesSomething);
w2.onmessage = function () { /* ... */};

两者都将是异步的,不会阻塞您用户的界面,并且它们都能够完成自己的工作,并且每个人都有自己的侦听器。

请注意 code 是一个 字符串 ,因此,如果您有一个函数,您可以使用它 .toString()() 连接,像这样:

function myWorkerContent () {
  // do something ....
}

const code = "(" + myWorkerContent.toString() + ")()";
// or, if you want to use templateLiterals
// const code = `(${myWorkerContent.toString()})()`;

这样,当 运行 你的 worker 时,它会立即在你的每个 worker 中创建并执行函数。

我看不出有什么方法可以做你想做的事。最初看起来很有希望的方法最终 运行 变成了难题。

Service Worker 和 fetch

在评论中,您建议使用服务工作线程作为可能的解决方案。我见过的服务人员示例提到提供 "custom responses to requests"。但是,所有示例都使用 fetch event to provide the custom response. AFAIK, it is produced only when you actually use the fetch API specifically. An xhr won't generate a fetch event. (Yes, I've tried it and it did not work.) And you cannot just use fetch in your specific situation instead of xhr because fetch does not operate synchronously. The specs for fetch mention a "synchronous flag", but it is not part of the API.

请注意,fetch API 和关联的事件并非 特定于 服务人员,因此您可以在普通情况下使用 fetch工人,或其他地方,如果它解决了你的问题。您经常看到 fetch 提到 service worker,因为 service worker 可用于无法使用常规 worker 的场景,其中一些场景需要为 fetch 请求提供自定义响应。

伪造的 XMLHttpRequest

Marinos 在 中建议使用伪造的 XMLHttpRequest 对象。 在大多数情况下,这是可行的。像 Sinon 这样的测试框架提供伪造的 XMLHttpRequest,允许测试代码完全控制被测代码获得的响应。但是,它不适用于您的用例场景。如果你的假 xhr 实现是作为一个 JavaScript 对象实现的,并且你尝试将它发送给工作人员,工作人员将获得它的完整克隆。在 worker 内部对假 xhr 执行的操作不会在 worker 外部看到。在 worker 外部执行的假 xhr 操作不会在 worker 内部看到。

理论上可以通过让伪造的 xhr 包含两个对象来解决克隆问题:一个是执行请求的前端,另一个是建立伪造响应的后端。您可以将前端发送给工作人员,但前端和后端必须相互通信,这让您又回到了您试图解决的通信问题。如果你能让伪造的 xhr 的两个部分以允许你伪造同步 xhr 请求的方式相互通信,那么 同理 你将能够解决通信问题没有假 xhr 的问题。