Передача объектов веб-работнику
Я пытаюсь передать объект веб-работнику через функцию postMessage.
Этот объект представляет собой квадрат, в котором есть пара функций для рисования на холсте и некоторых других вещах. Веб-работник должен вернуть массив этих объектов.
Проблема в том, что когда я вызываю функцию postMessage с этим объектом, я получаю эту ошибку:
Uncaught Error: DATA_CLONE_ERR: DOM Exception 25
Я получаю и отправку объекта рабочему, и наоборот.
Я думаю, что ошибка в том, что javascript должен сериализовать объект, но не может сделать это, потому что объект имеет встроенные функции.
У кого-нибудь когда-нибудь была подобная проблема? Вы знаете какой-то обходной путь к этому?
Заранее спасибо.
9 ответов
Есть несколько причин, по которым указанная вами ошибка могла быть выдана, причины перечислены здесь.
При отправке объектов веб-работникам объект сериализуется, а затем десериализуется в веб-работнике, если объект является сериализуемым объектом.
Это означает, что методы для объектов, которые вы отправляете веб-работнику, не могут быть переданы веб-работнику (вызывая ошибку, с которой вы столкнулись), и вам нужно будет предоставить необходимые методы / функции для объектов на стороне веб-работника и убедитесь, что они не являются частью объекта, который передается веб-работнику (-ям).
Как вы подозревали, объекты с функциями не могут быть опубликованы. То же самое касается объектов с рекурсивными ссылками, но в последнее время это изменилось в некоторых браузерах. Вместо того, чтобы рисковать делать ручную и дорогостоящую избыточную сериализацию для каждого сообщения, вы можете выполнить тест в начале вашего сценария, чтобы определить, какие функции использовать для отправки / получения данных.
У меня была та же проблема, и я решил ее, переместив почти весь код в рабочий и просто сохранив рендерер (оборачивая 2d-контекстный рендер) в основной поток. В работнике я сериализирую различные вызовы отрисовки, предназначенные для холста, в просто числа в (типизированном) массиве. Затем этот массив публикуется в главном потоке.
Так, например, когда я хочу нарисовать изображение, я вызываю drawImage()
метод на моем рабочем экземпляре рендерера в рабочем. Звонок переводится в нечто вроде [13,1,50,40]
что соответствует enum метода рисования, уникальному идентификатору изображения и его координатам xy. Несколько вызовов буферизуются и помещаются в один массив. В конце цикла обновления массив публикуется в основном потоке. Принимающий экземпляр основного средства визуализации анализирует массив и выполняет соответствующие вызовы отрисовки.
Когда вы передаете данные веб-работнику, копия данных создается с помощью алгоритма структурированного клонирования. Это указано в HTML5 (см. § 2.9: Безопасная передача структурированных данных).
MDN имеет обзор поддерживаемых типов. Поскольку функции не поддерживаются, попытка клонировать объекты, содержащие функции, будет DATA_CLONE_ERR
исключение.
Что делать, если у вас есть объект с функциями?
Если функции не актуальны, попробуйте создать новый объект, содержащий только те данные, которые вы хотите перенести. Пока вы используете только поддерживаемые типы, отправка должна работать. С помощью
JSON.stringify
а такжеJSON.parse
может также использоваться в качестве обходного пути, так какstringify
игнорирует функции.Если функции актуальны, переносного способа нет. Есть попытки использовать комбинацию
toString
а такжеeval
(например, используется библиотекой jsonfs), но это работает не во всех случаях. Например, он сломается, если ваша функция будет нативным кодом. Также закрытие проблематично.
Я недавно столкнулся с этой же проблемой при использовании веб-работников. Все, что я передавал своему работнику, сохраняло все его свойства, но загадочным образом теряло все свои методы.
Вы должны будете определить методы в самом скрипте веб-работника. Одним из обходных путей является importScripts
определение класса и вручную установить __proto__
свойство всего, что вы получаете. В моем случае я хотел пройти grid
объект, определенный в grid.js
(да, я работал на 2048), и сделал это так:
importScripts('grid.js')
onMessage = function(e) {
e.data.grid.__proto__ = Grid.prototype;
...
}
Настоящая проблема с объектами и веб-работниками заключается в методах этих объектов. У объекта не должно быть методов, только свойства.
Пример:
var myClass = function(){
this.a = 5;
this.myMethod = function(){}
}
var notParseableObject = new myClass();
var myClass2 = function(){
this.a = 5;
}
var parseableObject = new myClass2();
Первый не будет работать (с упомянутым сообщением об ошибке) с postMessage, а второй будет работать.
Взгляните на плагин vkThread
http://www.eslinstructor.net/vkthread/
он может передавать функцию работнику, включая функцию с контекстом (метод объекта). Он также может передавать функции с зависимостями, анонимными функциями и лямбдами.
--Vadim
Некоторые типы объектов, такие как ArrayBuffer и ImageBitmap, которые имеют реализуемый интерфейс Transferable и могут передаваться без копирования Object.
Это очень полезно в контексте Canvas + Web работник, потому что вы можете сэкономить время копирования данных между потоками.
Другой способ справиться с этим (поскольку я столкнулся с этим вопросом десять лет спустя, когда мне нужно было сделать это самостоятельно) - определить статический clone()
функция вашего класса, которая создает новый объект из (свойств) старого; тогда вы можете просто сказать
MyClass cloneObj = MyClass.clone(evt.data.myObj);
в начале вашего работника, чтобы получить «настоящий» объект типа MyClass, для которого вы можете затем вызывать методы из своего работника.
Если вы хотите передать объект методами, вы можете преобразовать его в строку и проанализировать на принимающей стороне.
postMessage(JSON.stringify(yourObject)
В слушателе
this.worker.addEventListener('message', (event) => {
const currentChunk = JSON.parse(event.data);
});