简体   繁体   English

在 Web-Worker 中同步等待消息

[英]Synchronously Wait for Message in Web-Worker

Is there some way to synchronously wait for or check for a new message in a web-worker?有什么方法可以同步等待或检查网络工作者中的新消息吗?

I have a large complicated body of code (compiled LLVM from emscripten) that I cannot refactor around callbacks.我有大量复杂的代码(从 emscripten 编译的 LLVM),我无法围绕回调进行重构。

I need to make sure that code after a certain line doesn't execute until I receive and handle a message from the UI-thread.我需要确保在我收到并处理来自 UI 线程的消息之前,某一行之后的代码不会执行。 If I block with a while-loop, the event-loop never runs so I can't receive messages.如果我用 while 循环阻塞,事件循环永远不会运行,所以我无法接收消息。

This is an issue that I also ran into while working with Pyodide .这是我在使用Pyodide时也遇到的问题。 I wanted to 'synchronously' call a function from the main thread.我想从主线程“同步”调用一个函数。

One solution involves the Atomic s and SharedArrayBuffer APIs.一种解决方案涉及AtomicSharedArrayBuffer API。 From the perspective of the web worker, this looks like the following从 web worker 的角度来看,这看起来像下面这样

  1. postMessage the main thread postMessage主线程
  2. freeze ourselves with Atomics.waitAtomics.wait冻结我们自己
  3. get unfrozen by the main thread被主线程解冻
  4. read the result from a SharedArrayBuffer .SharedArrayBuffer读取结果。 We can't receive the result as a postMessage , because there isn't a synchronous way of asking "did I get a message".我们无法以postMessage的形式接收结果,因为没有一种同步方式来询问“我收到消息了吗”。

Of course this requires a fair amount of extra code to handle all the serialization, data passing and more.当然,这需要相当多的额外代码来处理所有的序列化、数据传递等等。

The main limitation is that to use those APIs, one needs to have the COOP/COEP headers set .主要限制是要使用这些 API,需要设置 COOP/COEP 标头 The other bit to keep in mind is that this only works on recent browsers such as Safari 15.2 (released in December 2021).要记住的另一点是,这只适用于最近的浏览器,例如 Safari 15.2(2021 年 12 月发布)。

There is also an alternative solution with synchronous XHR and a service worker , but I haven't investigated that option.还有一个带有同步 XHR 和 service worker的替代解决方案,但我还没有研究过这个选项。

A crude hack to do this could be to use a synchronous API, such as the FileSystem API, and use a temporary file as a synchronising mechanism.一个粗略的破解方法可能是使用同步 API,例如 FileSystem API,并使用临时文件作为同步机制。

In your example, the emscripten code could issue a synchronous read on a file, while the UI thread writes to the same file, effectively unblocking the worker.在您的示例中,emscripten 代码可以对文件发出同步读取,而 UI 线程写入同一个文件,从而有效地解除阻塞工作人员。

No, unfortunately.不,不幸的是。 There was some discussion of adding a way to block on a message from the parent page, but it went nowhere.有一些关于添加一种方法来阻止来自父页面的消息的讨论,但它没有成功。 I'll see if I can dig up the relevant mailing list thread.我会看看我是否可以挖掘相关的邮件列表线程。

Can you break the sequence as follow without refactoring?你能在不重构的情况下打破如下顺序吗?

wrk.onmessage = function(e) {
    if (e.data.step === 'initial input') {
        doInitialWork(e.data.inputs)
    } else if (e.data.step === 'ui input') {
        doTheRest(e.data.uiData)
    }
}

The onmessage event will be blocked until the initial step execution stack is complete and the critical ui bits will only launch when and if the info is available. onmessage 事件将被阻止,直到初始步骤执行堆栈完成,并且关键 ui 位仅在信息可用时启动。

This is how I've done this.我就是这样做的。 It's really sorrowful to think that Web Workers haven't received the await message functionality, which would make the whole inter-thread communication concept a lot closer to Go language for example.想到 Web Workers 还没有收到await 消息功能,真的很可悲,这将使整个线程间通信概念更接近例如Go语言。 But no, they ain't did it, so have to invent stuff in order to keep threads synchronised但是不,他们没有这样做,所以必须发明一些东西来保持线程同步

  let resolver: any = undefined;
  let message: any = undefined;
  const messagePromise = new Promise((resolve) => resolver = resolve);

  worker.onmessage = async ({ data: payload }: { data: any }) => {
    message = payload;
    worker.onmessage = undefined;
    resolver?.();
  }

  await messagePromise;

Now, this is Typescript.现在,这是打字稿。 Just remove the types to get Javascript.只需删除类型即可获取 Javascript。 Note that there is no way to capture a message if it had been sent before we started listening for messages请注意,如果在我们开始侦听消息之前已发送消息,则无法捕获该消息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM