使用同步XMLHttpRequest从GUI获取数据的工作程序

8
我希望一个深入调用堆栈的Web Worker能够发起同步请求以从GUI获取信息。
GUI本身没有被阻塞 - 它能够处理消息。但是,工作线程堆栈上的JavaScript并没有采用async / await风格编写。它只是大量的同步代码。因此,如果GUI尝试使用postMessage向工作线程发送响应,那么该响应将会被卡在onmessage()队列中。 我至少找到了一种在今天的浏览器中有效的方法。工作线程可以向GUI发送postMessage以获取所需信息,并附带某种ID(例如UUID)。然后,它可以通过不会在工作线程中被弃用的同步XMLHttpRequest向外部网络服务器发送带有该ID的请求。
当工作线程等待http请求时,GUI处理信息请求。完成后,它将进行XMLHttpRequest以POST到同一服务器,并提供ID和数据。然后,服务器使用该信息来满足其保持对工作线程开放的阻塞请求。这样就满足了同步请求。
似乎将GUI和工作线程之间的同步外包给服务器有些脑残。但如果必须这样做,我会这样做,因为强制工作线程代码采用异步方式不适合使用情况。此外,我假设将来浏览器将能够本地执行这种同步。但看起来可以使用的唯一机制SharedArrayBuffer,已经暂时被禁用。

2018年底左右更新: SharedArrayBuffer在Chrome桌面版v67中被重新启用。但是在Android Chrome或其他浏览器上还没有恢复,并且可能需要一段时间。

(更奇怪的选项,比如将JavaScript解释器编译到worker中,以便可以随意暂停和重新启动JS堆栈,这些不在讨论范围内--不仅因为大小和性能,而且由于无法使用浏览器的开发工具调试worker。)

所以...

  • 有没有办法欺骗同步XMLHttpRequest,使其请求来自浏览器内部的东西(也许通过自定义链接方案?)如果GUI线程可以直接回答XMLHttpRequest,那就可以省去中间人。

  • 是否可以通过某种插件提供相同的功能?我想也许可以将同步作为一个抽象层来完成。如果某些人没有该插件,则会退回到使用网络作为同步代理。(并且假设他们重新启用SharedArrayBuffer,它只能使用它。)

我还想知道是否有一些JS-ready服务实现了回声服务器的协议...如果有人知道的话。似乎很容易写。


1
@MarinosAn 我认为这样的模拟无法满足OP所需的功能。问题在于,相同的模拟对象必须存在于启动工作进程的脚本和工作进程中。那么,你只需要将模拟发送给工作进程,对吗?不,因为传递给工作进程的对象是克隆的。因此,在工作进程内部进行的任何模拟请求都不会被工作进程外部的模拟看到。 - Louis
1
@HostileFork 我看到的服务工作者示例都提到了“对请求提供自定义响应”,但所有示例都使用fetch事件来提供自定义响应。据我所知,只有在您实际使用fetch API时才会生成它。xhr不会生成fetch事件。而且,您不能仅仅因为fetch不支持同步操作就在特定情况下使用fetch代替xhr。规范中提到了一个“同步标志”,但它不是API的一部分 - Louis
@Louis,你的评论帮助我省去了研究 service worker 的时间...如果你想在赏金截止日期之前把它们转化成一个“你没那么幸运”的文章,*(如果你恰好对我描述的 XMLHttpRequest 服务有任何见解)*,我会授予你积分,如果这是不幸的答案...! - HostileFork says dont trust SE
@HostileFork 完成。但恐怕我没有任何特别的见解可以补充到我的答案中。 - Louis
@Sando,这不是要重写任何特定的代码以异步方式。问题在于尝试为任意客户提供API服务,其中明确的目标是使他们能够以同步方式编码。仅仅因为JavaScript在允许编程风格变化方面相当柔弱,并不意味着世界上每个程序员都需要屈服于JavaScript的设计(或缺乏设计)。无论如何,显然我没有表达清楚:问题就是现在这样,而且就是这个问题。 - HostileFork says dont trust SE
显示剩余3条评论
2个回答

2
我看不到实现你想要的功能的方法。一些表面上看起来很有前途的方法最终都会遇到难以解决的问题。
服务工作者和fetch 在一条评论中,你提到了服务工作者作为可能的解决方案。我见过的服务工作者示例都提到了提供“自定义响应请求”的功能。但是,所有示例都使用fetch事件提供自定义响应。据我所知,它只在你专门使用fetchAPI时产生。xhr不会生成fetch事件。(是的,我已经尝试过了,但它不起作用。)并且你不能仅仅因为fetch不支持同步操作就在你特定的情况下使用fetch替换xhr。fetch的规范中提到了“同步标志”,但它不是API的一部分
请注意,fetch API及其相关事件并不是专门针对服务工作者的,因此如果它解决了您的问题,您可以在普通工作者或其他地方使用fetch。由于服务工作者可用于常规工作者无法使用的场景,并且其中一些场景涉及为fetch请求提供自定义响应,因此通常会看到与服务工作者一起提到fetch

模拟XMLHttpRequest

Marinos An在评论中建议使用虚假的XMLHttpRequest对象。在大多数情况下,这将有效。像Sinon这样的测试框架提供了虚假的XMLHttpRequest,允许测试代码完全控制被测试代码获得的响应。但是,它不适用于您的用例场景。如果您的虚假xhr实现作为一个JavaScript对象实现,并尝试将其发送到工作者,则工作者将获得完整的克隆。在工作者内部执行的虚假xhr上的操作将不会在工作者外部看到。在工作者外部执行的虚假xhr上的操作将不会在工作者内部看到。
理论上可以通过使用两个对象来解决克隆问题,一个是前端用于执行请求,另一个是后端用于建立虚假响应。你可以将前端发送到工作线程中,但前端和后端必须相互通信,这会让你重新面临你试图解决的通信问题。如果你能让虚假xhr的这两部分以一种允许你伪造同步xhr请求的方式进行通信,那么同样的道理,你也能够解决通信问题而不需要虚假xhr。

我提到Service Workers的原因是因为曾经有一些关于它们与XMLHttpRequest合作的内容。但显然这已不再可用。(https://www.fxsitecompat.com/en-CA/docs/2015/xmlhttprequest-is-no-longer-available-in-service-workers/)。我想我的剩下的问题是,可以通过Chrome/Firefox插件、Flash控件等方式来弥补缺陷,直到SharedArrayBuffer或其他解决方案问世。到那时,这个问题可能需要接受一个新答案,但看起来你的回答目前是最好的! - HostileFork says dont trust SE
唉,我对于插件或Flash的使用并不能提供太多建议。在考虑使用插件或Flash之前,我愿意尝试很多其他的解决方法,包括将工作器改写为异步工作。 - Louis
我正在处理一个类似 OP 的问题。我的实验表明,Service Worker 确实 响应同步 XHRs,但只有当它们是从 Web Worker 发送而不是主 GUI 线程发送时。当你说“xhr 不会生成 fetch 事件。(是的,我试过了,但没有成功。)”时,我怀疑你要么是在主线程中尝试请求,要么是浏览器最近更改了某些东西。 - Alex Hall
1
那是三年半之前的事了。我不太记得当时是如何测试的,而且浏览器在处理这个特定问题方面可能已经发生了变化。 - Louis

0
嗯...也许你可以像这样动态创建你的工作线程。
function startNewWorker (code) {
  var blob = new Blob([code], {type: "application/javascript"});
  var worker = new Worker(URL.createObjectURL(blob));
}

然后,对于每个新的HTTP请求,您需要启动自己的工作线程:

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()})()`;

这样,当运行您的工作程序时,它将在每个工作程序内立即创建和执行该函数。


1
如问题所述,我并没有GUI被阻塞的问题。问题在于工作线程想要从GUI获取信息,而不必解开其堆栈并“yield”到其onmessage循环以获取该信息。生成另一个工作线程并不能真正解决问题,除非有一些特权模式的工作线程间通信可以在阻塞时使用(我在这里试图寻找可能回答同步XMLHttpRequest的东西,但看起来没有结果)。 - HostileFork says dont trust SE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接