web-dev-qa-db-fra.com

Passer des objets à un travailleur Web

J'essaie de transmettre un objet à un travailleur Web via la fonction postMessage.
Cet objet est un carré qui a plusieurs fonctions pour se dessiner sur une toile et quelques autres choses. Le travailleur Web doit renvoyer un tableau de ces objets.
Le problème est que lorsque j'appelle la fonction postMessage avec cet objet, j'obtiens une erreur:

Uncaught Error: DATA_CLONE_ERR: DOM Exception 25

Je reçois à la fois l'envoi de l'objet au travailleur et l'inverse.
Je pense que l'erreur est due au fait que javascript doit sérialiser l'objet, mais ne peut pas le faire car cet objet possède des fonctions intégrées.

Est-ce que quelqu'un a déjà eu un problème similaire? Connaissez-vous des travaux à ce sujet?
Merci d'avance.

49
dgiulian

Il y a quelques raisons pour lesquelles l'erreur que vous avez mentionnée aurait pu être émise, les raisons sont énumérées ici .

Lors de l'envoi d'objets aux travailleurs Web, l'objet est sérialisé, puis désérialisé dans le travailleur Web s'il s'agit d'un objet sérialisable. 

Cela signifie que les méthodes pour les objets que vous envoyez à votre travailleur Web ne peuvent pas être transmises à ce dernier (ce qui provoque l'erreur que vous avez rencontrée) et vous devrez fournir les méthodes/fonctions nécessaires aux objets du côté de l’environnement du travailleur Web et assurez-vous qu’ils ne font pas partie de l’objet transmis au (x) travailleur (s) Web.

48
erikvold

Comme vous le soupçonniez, les objets avec des fonctions ne peuvent pas être postés. Il en va de même pour les objets avec des références récursives, mais cela a récemment changé dans certains navigateurs. Au lieu de risquer une sérialisation redondante manuelle et coûteuse pour chaque publication, vous pouvez effectuer un test au début de votre script pour déterminer les fonctions à utiliser pour l'envoi/la réception de données.

J'ai eu le même problème et je l'ai résolu en déplaçant la quasi-totalité du code dans le serveur de travail et en conservant simplement un rendu (enveloppant le rendu de contexte 2D) dans le fil principal. Dans l'ouvrier, je sérialise les différents appels de dessin destinés à la zone de travail en des nombres uniquement dans un tableau (typé). Ce tableau est ensuite publié dans le thread principal.

Ainsi, par exemple, lorsque je veux dessiner une image, j'appelle la méthode drawImage() sur mon instance de rendu de travailleur dans le travailleur. L'appel est traduit en quelque chose comme [13,1,50,40] qui correspond à l'énumération de la méthode draw, à l'id unique d'image et à ses coordonnées xy. Plusieurs appels sont mis en mémoire tampon et placés dans le même tableau. À la fin de la boucle de mise à jour, le tableau est publié dans le thread principal. L'instance de rendu principale de réception qui reçoit analyse le tableau et effectue les appels de dessin appropriés.

12
bennedich

J'ai récemment rencontré le même problème lors de l'utilisation de travailleurs Web. Tout ce que je transmettais à mon ouvrier conservait toutes ses propriétés, mais perdait mystérieusement toutes ses méthodes.

Vous devrez définir les méthodes dans le script de travail Web lui-même. Une solution de contournement consiste à importScripts la définition de la classe et à définir manuellement la propriété __proto__ de tout ce que vous recevez. Dans mon cas, je voulais passer un objet grid, défini dans grid.js (et oui, je travaillais sur 2048), et je l'ai fait comme suit: 

importScripts('grid.js')

onMessage = function(e) {
  e.data.grid.__proto__ = Grid.prototype;
  ...
}
6
xuanji

Le vrai problème avec object et webworkers est avec les méthodes de ces objets. Un objet ne doit pas avoir des méthodes mais uniquement des propriétés.

Ex:

var myClass = function(){
    this.a = 5;
    this.myMethod = function(){}
}
var notParseableObject = new myClass();


var myClass2 = function(){
    this.a = 5;
}
var parseableObject = new myClass2();

Le premier ne fonctionnera pas (avec le message d'erreur mentionné) avec postMessage et le second fonctionnera.

4
José Cabo

Lorsque vous transmettez des données à un travailleur Web, une copie des données est réalisée à l'aide de algorithme de clone structuré . Il est spécifié en HTML5 (voir § 2.9: Transmission sécurisée de données structurées ).

MDN a un aperçu des types pris en charge . Comme les fonctions ne sont pas prises en charge, essayer de cloner des objets contenant des fonctions lève donc une exception DATA_CLONE_ERR.

Que faire si vous avez un objet avec des fonctions?

  • Si les fonctions ne sont pas pertinentes, essayez de créer un nouvel objet contenant uniquement les données que vous souhaitez transférer. Tant que vous n'utilisez que des types pris en charge, l'envoi devrait fonctionner. L'utilisation de JSON.stringify et JSON.parse peut également être utilisée comme solution de contournement, car stringify ignore les fonctions.

  • Si les fonctions sont pertinentes, il n'y a pas de moyen portable. Il existe des tentatives d'utilisation d'une combinaison de toString et eval (par exemple, utilisée par la bibliothèque jsonfs ), mais cela ne fonctionnera pas dans tous les cas. Pour les instances, cela se cassera si votre fonction est du code natif. Les fermetures sont également problématiques.

2
Philipp Claßen

jetez un oeil au plugin vkThread

http://www.eslinstructor.net/vkthread/

il peut transmettre une fonction à un travailleur, y compris une fonction avec un contexte (méthode de l'objet). Il peut également passer des fonctions avec des dépendances, des fonctions anonymes et des lambdas. 

--Vadim

1
vadimk

Certains types d'objets tels que ArrayBuffer et ImageBitmap qui ont l'interface Transférable et peuvent être transférés sans copier l'objet. 

C’est très utile dans le contexte de Canvas + Web worker car vous pouvez économiser du temps de copie des données entre les threads. 

0
julian libor