awesome-everything EN
↑ Обратно к восхождению

Браузер и фронтенд-рантайм

Structured clone и transferables

Суть postMessage делает глубокую копию через structured clone (~1 мс/МБ) — transferables передают ArrayBuffer за O(1), передавая владение принимающему потоку.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 12 min

Вы переносите задачу обработки изображения на 40 МБ в воркер — и главный поток всё равно замирает на 40 мс, прежде чем воркер даже начнёт. Работа переехала, но данные — нет.

Алгоритм structured clone

postMessage(data) не делит data — он делает его глубокую копию через алгоритм structured clone. Structured clone обрабатывает объекты, массивы, Map, Set, Date, типизированные массивы, Blob и ArrayBuffer. Он не обрабатывает:

  • Функции (молча отбрасываются)
  • DOM-узлы (бросает исключение)
  • Прототипы классов — методы теряются, выживают только свойства данных

Копия синхронна на отправляющем потоке и масштабируется с размером payload-а: примерно 1 мс на МБ для плоских объектов, больше для глубоко вложенных структур. Отправьте объект на 50 МБ — и вы заблокировали отправляющий поток на ~50 мс — вы переместили работу, но заплатили за это налог на клонирование.

Стоимость structured clone
Скорость клона плоского объекта
~1 мс / МБ
Клон объекта на 30 МБ
~30 мс блокировки отправителя
Transfer (ArrayBuffer)
O(1), микросекунды
Transferable: ArrayBuffer
Отсоединяет отправителя (byteLength → 0)
Transferable: ImageBitmap
Передаётся, источник становится закрытым
Transferable: OffscreenCanvas
Эксклюзивное владение переходит

Transferables: передача владения за O(1)

Побег от стоимости клона — механизм transferable. Передайте второй аргумент в postMessage — список transferable-объектов:

postMessage({ pixels, width, height }, [pixels.buffer]);

Вместо копирования байтов браузер отдаёт нижележащую память принимающему потоку и отсоединяет её от отправителя. После передачи:

  • pixels.buffer.byteLength равен 0 у отправителя
  • Получатель имеет новый view с полными данными

Стоимость падает с O(size) до O(1). Transfer — правильный ход для больших бинарных payload-ов: bitmap-ы изображений, аудиобуферы, содержимое файлов.

Типы transferable: ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas, ReadableStream, WritableStream, TransformStream.

Что теряется при structured clone

При postMessage объекта с методами:

class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  distanceTo(other) { ... }
}
const p = new Point(1, 2);
worker.postMessage(p);
// Воркер получает: { x: 1, y: 2 } — метода distanceTo нет

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

Викторина

Вы `postMessage`-ите Float32Array на 40 МБ воркеру, и отправляющий поток замирает на ~40 мс. Каков фикс?

Викторина

`onmessage` воркера получает объект, у которого были методы до отправки. Методы пропали. Почему?

Закончи аналогию

Когда вы postMessage-ите большой ArrayBuffer, по умолчанию делается глубокая копия (налог structured clone). Альтернатива перемещает владение буфером к принимающему потоку за константное время, оставляя его непригодным у отправителя. Как называется эта альтернатива?

Посчитай

Structured clone плоского объекта стоит примерно 1 мс на МБ. Воркеру шлётся объект на 30 МБ по значению. Примерно сколько миллисекунд отправляющий поток блокируется на клоне?

мс

Передать буфер воркеру вместо клонирования

1/3
Какой RFC?

Какая спецификация определяет алгоритм structured clone, используемый postMessage, IndexedDB и Cache API?

Почему это работает

Почему structured clone синхронен и почему он блокирует отправителя? Потому что спецификация JavaScript гарантирует: вызов postMessage — единственная, атомарная операция: данные полностью сериализуются до возврата функции. Если бы сериализация была асинхронной, вы могли бы мутировать данные между вызовом postMessage и моментом, когда воркер их читает, нарушая изоляцию. Синхронная стоимость — цена гарантии изоляции. Механизм transfer обходит это, перемещая указатель памяти вместо байтов — ОС может атомарно передать владение регионом памяти без копирования.

Вспомните перед уходом
  1. 01
    Что structured clone обрабатывает и что отбрасывает?
  2. 02
    Как передать ArrayBuffer вместо клонирования, и что происходит со ссылкой отправителя?
  3. 03
    Когда следует использовать SharedArrayBuffer вместо transfer?
Итог

postMessage делает глубокую копию данных через алгоритм structured clone со скоростью ~1 мс на МБ — объект на 30 МБ блокирует отправляющий поток на ~30 мс, сводя на нет пользу от переноса работы вне главного потока. Фикс — механизм transferable: поместите ArrayBuffer во второй аргумент postMessage, и браузер отдаёт владение получателю за O(1), отсоединяя ссылку отправителя. Функции и методы прототипов не могут пересекать границу — structured clone это сериализатор данных, не копировщик объектов. Для случаев, когда оба потока нуждаются в параллельном доступе к одной памяти, ответ — SharedArrayBuffer, но он требует cross-origin isolation.

Связанные уроки
встречается в41
Продолжить восхождение ↑Жизненный цикл service worker и стратегии кеширования
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.