typescript BroadcastChannel API在调用postMessage()时关闭时不一致地抛出错误

gcuhipw9  于 2023-06-24  发布在  TypeScript
关注(0)|答案(1)|浏览(228)

在使用BroadcastChannel API时,我意识到如果通道关闭,开发人员仍然尝试调用postMessage(),则不会抛出任何异常;至少不是一直这样

第一个案例

close()从与postMessage()相同的broadcastChannel示例调用,抛出错误:

const bc1 = new BroadcastChannel('foo');
const bc2 = new BroadcastChannel('foo');

bc1.postMessage('bar');

bc2.addEventListener('message', (event) => console.log(event.data));

setTimeout(() => {
  bc1.close(); // we're calling the close() from bc1, same instant where we will call postMessage later
}, 500);

setTimeout(() => {
  try {
    bc1.postMessage('bar2');
  } catch (error) {
    console.error('postMessage error', error);
  }
}, 1000);

上面的代码会抛出一个异常:

postMessage error
Error: Failed to execute 'postMessage' on 'BroadcastChannel': Channel is closed

第二种情况

close()是从另一个broadcastChannel示例调用的postMessage(),不会抛出错误:

const bc1 = new BroadcastChannel('foo');
const bc2 = new BroadcastChannel('foo');

bc1.postMessage('bar');

bc2.addEventListener('message', (event) => console.log(event.data));

setTimeout(() => {
  bc2.close(); // we're calling the close() from bc2, NOT the same instant where we will call postMessage later
}, 500);

setTimeout(() => {
  try {
    bc1.postMessage('bar2');
  } catch (error) {
    console.error('postMessage error', error);
  }
}, 1000);

上面的代码没有抛出任何异常。在我看来,它应该抛出一个异常。

提问

在第二种情况下,是否有任何可能的方法来检测通道是否已经关闭?或者如果这是一个bug,我可以在哪里提交罚单?
如果你想玩MRE,这里是link

sczxawaw

sczxawaw1#

在阅读了文档之后,我认为这是一个可以接受的行为。首先,这里有一个来自官方规范的注解:
强烈建议作者在不再需要BroadcastChannel对象时显式关闭它们,以便对它们进行垃圾回收。创建许多BroadcastChannel对象并丢弃它们,同时将它们保留在事件侦听器中,而不关闭它们,这可能会导致明显的内存泄漏,因为只要这些对象具有事件侦听器(或者直到它们的页面或worker被关闭),这些对象就会继续存在。
显然,我们需要清理BroadcastChannel示例以防止内存泄漏。现在,进一步思考,如果我们将消息广播到不同的上下文以同步/交换信息,我们需要在那里创建具有相同频道名称的示例。然而,其他情况可能很容易关闭,这导致为该特定情况清理资源。尽管如此,其他环境应该继续交换信息--表演必须继续。
总之,我认为不抛出异常是完全可以的,因为BroadCastChannel并不知道它的所有示例。为了解决这个问题,您可以创建一个close处理函数,它将被放置在每个BroadcastChannel侦听器中。例如:

bChannel.onmessage = function (event) {
  if (event.data == 'close'){
    // clear something...
    bChannel.close();
  }
}

然后你可以调用bChannel.postMessage('close');这将关闭所有示例(将关闭标志设置为假)。在此之后,他们应该抛出错误。

相关问题