Есть ли способ ответить только отправителю после получения сообщения BroadcastChannel?

Предположим, у меня есть несколько окон или вкладок A, B, C, D и E с одинаковым происхождением, которые не содержат ссылок друг на друга. (например, пользователь открыл их самостоятельно). Предположим, A отправляетBroadcastChannelсообщение к другим, и в результате, D должен послать некоторые данные обратно в A, в идеале, не вовлекая B, C или E.

Возможно ли это, используя какой-либо из API передачи сообщений?

Есть event.source свойство в событии широковещательного сообщения, которое выглядело так, как будто оно могло содержать WindowProxy или MessagePortобъект в этом контексте, но (по крайней мере, в моих тестах с Firefox 78) он был просто нулевым. Также естьports массив, но он был пуст.

... Я знаю, что вы можете запустить SharedWorker, чтобы назначить каждому окну уникальный идентификатор и действовать как промежуточную станцию ​​для передачи сообщений между ними, но (а) это кажется очень сложным для желаемой функциональности и (б) каждый сообщение, отправленное таким образом, потребует 2 перехода, от окна к sharedWorker и обратно к окну, оба раза пересекая границы потока и (обычно) сериализуясь и десериализуясь также оба раза - даже когда два окна используют один и тот же поток javascript! Так что это не очень эффективно.

This seems like such an obvious thing to want to do, I'm finding it hard to believe there isn't something obvious I'm missing... but I don't see it, if so!

2 ответа

Решение

Похоже, стандарты требуютsource быть нулевым для BroadcastChannel. Но он разделяет интерфейс MessageEvent с несколькими другими API, которые используютsource, поэтому он существует, но имеет значение null.

Шаги метода postMessage(message):
...
5. Удалить источник из мест назначения.

Похоже, они намеренно сохранили BroadcastChannelочень легкий. Только предположение, но для функциональности, которую вы ищете, могли потребоваться дополнительные ресурсы, которые они не хотели выделять. Это предположение основано на общем примечании, которое они имеют в спецификации:

Для сложных случаев, например, для управления блокировкой общего состояния, для управления синхронизацией ресурсов между сервером и несколькими локальными клиентами, для совместного использования соединения WebSocket с удаленным хостом и т. Д., Общие рабочие процессы являются наиболее подходящим решением.

Однако в простых случаях, когда совместно используемый воркер представляет собой необоснованные накладные расходы, авторы могут использовать простой механизм вещания на основе канала, описанный в этом разделе.

SharedWorkers определенно более подходят для сложных случаев, думайте о BroadcastChannel как о простом отправителе уведомлений "один ко многим".

Он не может передавать данные. Кто из получателей должен стать владельцем?- так что, за исключением случаев с Blob-объектами (которые представляют собой просто небольшие обертки без собственных данных), передача данных через BroadcastChannel означает, что они должны быть полностью десериализованы всеми получателями, а не самый эффективный способ сделать.

Поэтому я не уверен, какие данные вам нужно отправлять, но если это большие данные, которые обычно можно передавать, то, вероятно, предпочтительнее SharedWorker.

Один обходной путь, если ваши данные не должны передаваться, - это создать новый BroadcastChannel, который будут прослушивать только два ваших контекста.

Живая демонстрация

На странице A:

const common_channel = new BroadcastChannel( "main" );
const uuid = "private-" + Math.random();
common_channel.postMessage( {
  type: "gimme the data",
  from: "pageB",
  respondAt: uuid
} );
const private_channel = new BroadcastChannel( uuid );
private_channel.onmessage = ({data}) => {
  handleDataFromPageB(data);
  private_channel.close();
};

На странице B:

const common_channel = new BroadcastChannel( "main" );
common_channel.onmessage = ({ data }) => {
  if( data.from === "pageB" && data.type === "gimme the data" ) {
    const private_channel = new BroadcastChannel( data.respondAt );
    private_channel.postMessage( the_data );
    private_channel.close();
  }
};

Относительно того, почему у вас не может быть portsvalue в событии MessageEvent, срабатывающем на BroadcastChannels, потому что MessagePorts должны быть переданы, но, как мы уже сказали, BroadcastChannels не может выполнять передачу.
Почему нетsource, вероятно, потому, что, как вы и ожидали, это должен был быть объект WindowProxy, но WorkerContexts также может отправлять сообщения в BroadcastChannels, и они не реализуют этот интерфейс (например, их postMessage метод не будет делать то же самое, что для WindowContext).

Другие вопросы по тегам