I'm trying to pass an object to a web worker through the postMessage function.
This object is a square that has a couple of functions to draw himself on a canvas and some other things. The web worker must return an array of this objects.
The problem is that when I call the postMessage function with this object, I get an this error:
Uncaught Error: DATA_CLONE_ERR: DOM Exception 25
I get this both sending the object to the worker and the other way around.
I think the error is because javascript must serialize the object, but can't do it because the object has functions built-in.
Does anyone ever had a similar problem? Do you know some workarround to this?
Thanks in advance.
There are a few reasons why the error that you mention could have been thrown, the reasons are listed here.
When sending objects to web workers, the object is serialized, and later deserialized in the web worker if the object is a serializable object.
This means that the methods for the objects you send to your web worker are not something that can be passed to the web worker (causing the error that you have run into), and you will need to provide the necessary methods/functions to the objects on the web worker's side of the environment, and make sure they are not part of the object that is passed to the web worker(s).
As you suspected objects with functions cannot be posted. The same goes for objects with recursive references, but this has changed in some browsers lately. Instead of risking doing manual and costly redundant serialization for every post you can perform a test at the beginning of your script to determine which functions to use for sending/receiving data.
I've had the same problem and solved it by moving almost all code into the worker and just keeping a renderer (wrapping the 2d context renderer) in the main thread. In the worker I serialize the different draw calls meant for the canvas into just numbers in an (typed) array. This array is then posted to the main thread.
So for instance when I want to draw an image I invoke the drawImage() method on my worker renderer instance in the worker. The call is translated into something like [13,1,50,40] which corresponds to the draw method enum, image unique id and its xy coordinates. Multiple calls are buffered and put in the same array. At the end of the update loop the array is posted to the main thread. The receiving main renderer instance parses the array and perform the appropriate draw calls.
I recently encountered this same problem when using web workers. Anything I passed to my worker kept all its properties but mysteriously lost all its methods.
You will have to define the methods in the web worker script itself. One workaround is to importScripts the class definition and manually set the __proto__ property of anything you receive. In my case I wanted to pass a grid object, defined in grid.js (yup, I was working on 2048), and did it like so:
importScripts('grid.js')
onMessage = function(e) {
e.data.grid.__proto__ = Grid.prototype;
...
}
When you pass data to a web worker, a copy of the data is made with the structured clone algorithm. It is specified in HTML5 (see § 2.9: Safe passing of structured data).
MDN has an overview of supported types. As functions are not supported, trying to clone objects containing functions will therefore throw a DATA_CLONE_ERR exception.
What to do if you have an object with functions?
If the functions are not relevant, try to create a new object that contains only the data that you want to transfer. As long as you use only supported types, send should work. Using JSON.stringify and JSON.parse can also be used as a workaround, as stringify ignores functions.
If the functions are relevant, there is no portable way. There are attempts to use a combination of toString and eval (e.g., used by the jsonfs library), but this will not work in all cases. For instances, it will break if your function is native code. Also closures are problematic.
The real problem with object and webworkers is with the methods of that objects. A object should not have methods just properties.
Ex:
var myClass = function(){
this.a = 5;
this.myMethod = function(){}
}
var notParseableObject = new myClass();
var myClass2 = function(){
this.a = 5;
}
var parseableObject = new myClass2();
The first wont work (with the mentioned error message) with postMessage and the second will work.
Some type of objects like ArrayBuffer and ImageBitmap which have the Transferable interface implementet and can be transfered without copy the Object.
Thats very usefull in Context of Canvas + Web worker cause you can save the time of copy the data between the threads.
take a look at the vkThread plugin
http://www.eslinstructor.net/vkthread/
it can pass function to a worker, including function with context ( object's method ). It can also pass functions with dependencies, anonymous functions and lambdas.
--Vadim
Another way of handling this (as I come across this question a decade later having needed to do it myself) is to define a static clone() function on your class that constructs a new object from (the properties of) an old one; then you can simply say
MyClass cloneObj = MyClass.clone(evt.data.myObj);
at the start of your worker to get a 'real' object of type MyClass that you can then call methods on from within your worker.
if you want to pass the object with methods you can stringify it and parse it at the receiving end.
postMessage(JSON.stringify(yourObject)
In the listener
this.worker.addEventListener('message', (event) => {
const currentChunk = JSON.parse(event.data);
});
Related
I have a self-defined class that calls a native method to allocate buffer in the constructor method, like below:
MyClass = function () {
this.buffer = native.alloc()
}
The buffer has to be released when the instance of MyClass is being deleted.
Can I define a destructor in javascript like below? Would it be invoked when the GC happens?
MyClass.prototype.destructor = function () {
native.free(this.buffer)
}
If your question is whether there are native methods to do what you described, then the answer is -no. There are no such methods.
If your question - is whether it is possible to do it in any way, then the answer is - yes it is possible.
If you control the entire code base of the project, then no one bothers you to come up with an architecture that would allow you to delete objects using only your API. As a result, if this is your API then you can do whatever you want.
If you do not control the code, then there are several workarounds how to control the work with objects in JavaScript.
The first thing to try is a proxy. This api allows you to control all manipulations with any object, provided that you can replace the original link to the object with your link with a proxy.
The second thing to try is using Object.defineProperty. If the owner of the object did not specifically bother to close access to this API, then in fact you can hang on someone else's object any volume of your code that will allow you to control its behavior as you want.
As a result, you will be able to implement the functionality you are asking about.
I have an Object like that:
function A(id) {
this.id = id;
}
A.prototype.getId = function() {
return this.id;
}
It is included in a html-page as a file ("objects.js") as well as in the web worker with importScript("objects.js").
Now I create an instance of A in the html-page with "var a = new A()" and post it with "postMessage()" to a Web Worker.
The funny thing is that in the worker it still has the property "id" with its value but the prototype function is lost.
I guess the reason may be that the prototype functions are "bound" to the html-page context and not to the web worker context.
So what I'm doing in the worker is that:
event.data.a.__proto__ = A.prototype;
It's working and I see it as some kind of cast...
Now my question is if that is the only and a valid way or if there's a better way of posting an object with prototype functions to a web worker.
The object definition is available in both contexts...
The structure clone algorithm that is used for serializing data before sending it to the web worker does not walk the prototype chain (for details, see § 2.7.5 Safe passing of structured data). That explains why the derived functions are not preserved.
Beside manually restoring the object as you did, you could also creating a new object, which has the prototype functions, and use Object.assign to copy the properties from the received object.
Note that both workarounds assume that the prototype object and their functions are known to the web worker. In general, there is no automated way to transfer arbitrary objects while preserving functions (see my answer to this related question about sending objects with functions).
The specification for webworkers does not allow for anything but strings to be passed.
Here is a question about this.
So you should serialize the object data with (for example) json and then deserialize it on the other side, and thus creating a new instance of the object, with the same data, inside the webworker.
The same method can be used to pass the object back out again - but both of them must know how to create, serialize and deserialize object of type A.
I'm using JSON.stringify and JSON.parse to store and retrieve objects from localStorage. However, it appears that JSON.stringify strips out the instance functions from the object. Thus, after JSON.parse, I can no longer call myObject.doSomething(). I know that I can attach this function manually: myObject.doSomething = MyClass.prototype.myFunction, but that'll be troublesome if this action is repeated many times in the web app. How do people normally do this in JavaScript?
JSON obviously does not hold onto the functions themselves is only stores simple typed variables. The way I have addressed this in the pass is to be a restore method in my class and simply call that method with the data from JSON so as to re-populate the class with the data that belongs in it.
I have done this extensively with the Value Object ( VO ) design pattern in my code base and it has worked quite well for me. Just a word of a caution though, Ie7/Ie8 are not terribly friendly with this approach if you try to communicate across windows. As I recall I think it is IE7 that does not return the right "typeof" for some properties so I ran into a whole bunch of challenges in my restore when cross-window communication was involved.
I am working on a migration platform to migrate web applications from a device to another. I am extending it to add the support for preserving JavaScript state.
My main task is to create a file representing the current state of the executing application, to transmit it to another device and to reload the state in the destination device.
The basic solution I adopted is to navigate the window object and to save all its descendant properties using JSON as base format for exportation and extending it to implement some features:
preserving object reference, even if cyclic (dojox.json.ref library)
support for timers
Date
non-numericproperties of arrays
reference to DOM elements
The most important task I need to solve now is exportation of closures. At this moment I didn't know how to implement this feature.
I read about the internal EcmaScript property [[scope]] containing the scope chain of a function, a list-like object composed by all the nested activation context of the function. Unfortunately it is not accessible by JavaScript.
Anyone know if there is a way to directly access the [[scope]] property? Or another way to preserve the state of a closure?
This sounds like an impossible feat as you would need access to the references stored in each variable.
The best solution would probably be to first refactor your code into storing state on an available object - that way you could easily use JSON.stringify/parse to save/restore it.
So go from
var myFuncWithScope = (function() {
var variable = 0;
return function() {
return variable++;
}
})();
var serializedState = .... // no can do
to
var state = {
myScope = {
variable: 0
}
};
var myFuncWithoutScope = function(){
return state.myScope.variable++;
}
var serializedState = JSON.stringify(state);
From where are you executing? If you are a native app or web browser extension you may have some hope, via internal access to whichever scripting engine it's using. But from a script in web content, there is no hope.
[[Scope]] is one ECMAScript internal property that you cannot access or preserve from inside the interpreter, but far from the only one; almost all of the [[...]] properties are not accessible. Function code references, prototypes, properties, enumerability, owner context, listeners, everything to do with host objects (such as DOM nodes)... there are infinitely many ways to fail.
You can't preserve or migrate web applications without requiring them to follow some strict rules to avoid all but the most basic JS features.
I'm having trouble calling a non IDispatch method in an ActiveX control that I've written.
I have a web page with two separate Active X object both of which I've written. I start by calling a method on the first object which returns an interface pointer to a new COM object that is not co-creatable. I then call a method on this new object passing the second ActiveX object as the argument. Inside this method I call QueryInterface to obtain a private/internal interface pointer on my second ActiveX object. The problem is that the returned pointer from QueryInterface is not a valid pointer to my object, and any attempt to use it crashes.
How can I obtain a interface to my actual object that I can use? My private interface uses structures so it not compatible with IDispatch, and being an internal interface I do not desire to expose it at all in the type library.
It's a little hard to tell with just a description, but I assume that the method on the first object is returning an IDispatch pointer to the object it creates? The JScript environment will only be able to cope with that.
Also, is your implementation of QueryInterface valid? Does it work for you in non-scripting contexts?
I am still a little unclear on which objects have which interfaces, but if you have an object with an IDispatch-unfriendly interface, perhaps you can build a simple wrapper object to hold it, where the wrapper object has a proper IDispatch interface?
Apologies if I am way off the mark...I haven't wrestled with ActiveX stuff in a few months.