I have an array of collections which needs to be freezed using web-worker. Sample below shows single collection freezing.
var worker = new Worker("worker.js");
worker.onmessage = function (e) { // WATCH MESSAGES FROM THE WORKER
var data = e.data;
// TEST: freezed collection property changes here in main scope. Weird!!!
};
worker.postMessage(JSON.stringify({
'collection' : someHugeJSON_object
}));
// In my worker.js
function deepFreeze(){
// my freezing logic
}
onmessage = function (e) {
var data = JSON.parse(e.data);
freezedJSON_object = deepFreeze(data.collection);
// TEST: collection property does not change in worker scope after freezing
// DONE FREEZING EVERYTHING... PHEW!!!
postMessage({
'collection' : freezedJSON_object
});
}
Does enumerability, configurability, or writability properties of an object, restricted to a particular scope?
When you call postMessage(obj) you don't send obj - it's cloned using structured clone algorithm.
MDN page is rather explicit about what happens to frozen objects:
Property descriptors, setters, and getters (as well as similar metadata-like features) are not duplicated. For example, if an object is marked read-only using a property descriptor, it will be read-write in the duplicate, since that's the default condition.
So you can't freeze object in WebWorker and send it back to main thread.
By the way - you don't have to call JSON.stringify on messages passed to WebWorker.
Related
I'm working on a project that utilizes web workers. It seems that the workers are generating quite a bit of extra garbage that has to be collected from the message passing.
I'm sending three things to the worker via post message from the main thread. First is just a number, second is an array with 7 numbers, and 3rd is the date. The firs two are properties of an object as seen below. This is called every 16ms on RAF for about 20 objects. The GC ends up collecting 12MB every 2 seconds or so. I'm wondering if there is a way to do this without creating so much garbage? Thanks for any help!
//planet num (property of object) is just a number like: 1
//planetele looks like this (property of an object)
//[19.22942, 313.4868, 0.04441, 0.7726, 170.5310, 73.9893, 84.3234]
//date is just the date object
//posted to worker like so:
planetWorker.postMessage({
"planetnum": planet.num,
"planetele": planet.ele,
"date": datet
});
//the worker.js file uses that information to do calculations
//and sends back the planet number, with xyz coordinates. (4 numbers)
postMessage({data: {planetnum : planetnum, planetpos: planetpos}});
I tried two different avenues and ended up using a combination of them. First, before I sent some of the elements over I used JSON.stringify to convert them to strings, then JSON.parse to get them back once they were sent to the worker. For the array I ended up using transferable objects. Here is a simplified example of what I did:
var ast = [];
ast.elements = new Float64Array([0.3871, 252.2507, 0.20563, 7.005, 77.4548, 48.3305, 0.2408]);
ast.num = 1;
var astnumJ = JSON.stringify(ast.num); // Probably not needed, just example
// From main thread, post message to worker
asteroidWorker.postMessage({
"asteroidnum": astnumJ,
"asteroidele": ast.elements.buffer
},[ast.elements.buffer]);
This sends the array to the worker, it doesn't copy it, which reduces the garbage made. It is now not accessible in the main thread, so once the worker posts the message, you have to send the array back to the main thread or it wont be accessible as a property of ast anymore. In my case, because I have 20 - 30 ast objects, I need to make sure they all have their elements restored via post message before I call another update to them. I did this with a simple counter in a loop.
// In worker.js
asteroidele = new Float64Array(e.data.asteroidele); // cast to type
asteroidnum = JSON.parse(e.data.asteroidnum); // parse JSON
// Do calculations with this information in worker then return it to the main thread
// Post message from worker back to main
self.postMessage({
asteroidnum : asteroidnum,
asteroidpos : asteroidpos, // Calculated position with elements
asteroidele : asteroidele // Return the elements buffer back to main
});
// Main thread worker onmessage function
asteroidWorker.onmessage = function(e){
var data1 = e.data;
ast.ele = data1.asteroidele; // Restore elements back to ast object
}
Not sure this is the best approach yet, but it does work for sending the array to and from the worker without making a bunch of extra garbage. I think the best approach here will be to send the array to the worker and leave it there, then just return updated positions. Working on that still.
This question is not specific to this particular scenario. The scenario could be concerning any complex/deep JavaScript object but for me to visualize the question I need a scene. Do not answer specifically on the supplied example - only cloning, scope and objects.
Brief Outline
If I have stored a websocket object inside an object, could I later move this websocket out of the storage object and put into another object? kind of like when you pop or splice an array, the array item is not only removed from the array but it's also returned to you (not obliterated but transferred / 'plucked from'). Or is the websocket object stuck/tied to the storage object {}? (If so, in what state would the object be in a lower scope?? What is it 'then'?)
Obviously:
//pseudo code
var finalobject = Object.assign({},storageobject.socket);
//not in nodejs? probably a bad idea anyway
var finalobject = storageobject.socket;
//only a shallow reference
delete storageobject.socket
finalobject.test = 'abc123';
// Obvious TypeError: Cannot set property..bla bla
what would I have though if?:
function lowerscope(x){return x;}
var sameobject=lowerscope(storageobject.socket);
is sameobject now dolly the sheep? a shallow reference or a deep reference? a copy?
My Question And The reason Why I think it's not a stupid question
If I first assign the socket in the storage object to the finalobject
then delete the storageobject.socket then I cant use the finalobect or set properties because that was just a shallow reference to what I just deleted.
So what exactly is going on if I pass the finalobject through a function like so? (again pseudo code)
var finalobject = storageobject.socket;
function appInit(mySocket) {
// do app stuff here with mySocket
// set up some functions...
return; // * come back out to main scope
}
appInit(finalobject);
delete storageobject.socket; // * what am I deleting??
Is the object cloned into appInit? Or does it live there as just another reference?
Example of my code flow (optional; you can skip this):
This part is (too) specific to my case and is here only in case someone asks to see code or 'needs' to understand why I ask.
I have put/created-reference-to my newly created (node.js ws) Websocket in another object:
var uid = '7657rrfdt6e6t'; //unique id
var socketsReference = {}; //main scope
socketServer.on('connection',function(mainSocket) {
var socketsGroup = {}; //local scope
mainSocket.uid = uid;
socketsGroup[1] = mainSocket;
socketsReference[uid] = socketsGroup;
socketsGroup[1].send('uid:' + uid);
So that is the main socket that is stored in the object. It has just sent its uid to the client so that it can set that as its uid property too.
Then next thing I do is connect the client to a secondary socket. For this I want the secondary socket to end up with the similar uid+'.s2' unique id as the main so what I do is connect the second socket then send the new 2nd connection a message from the client to the server which tells the server side socket the uid of the mainSocket
secondarySocket.on('message', function(data) {
this.uid = data.uid + '.s2';
socketsReference[data.uid][2] = this;
Later in the application I take the correct user specific socket group and inject it into the main application where the connected user is in his own scope.
var mySockets = socketsReference[my_uid]
appInit(mySockets);
I want to have singleton kind of object whose value gets changed during multiple events across multiple pages. This object is used to bind the ui in various pages.
I have a 'main.js' file :
var obj = { flag : false } ;
// call back method
function click() {
obj.flag = true;
}
and in my next.js file
// call back method
function click() {
alert(obj.flag); // **alerts false** . It should alert **true** instead.
}
Is there a way to persist the value other than using the window.name property ? or am I following the wrong approach
You can use HTML5 localStorage.
As described in the documentations (Safari, Mozilla etc.), localStorage supports string key/value pairs.
Therefore you need to use JSON.stringify to save your object in the storage.
var obj = { flag : false };
// Save the object in the storage
localStorage.setItem('obj', JSON.stringify(obj));
// Get the object from storage
var objectData = localStorage.getItem('obj');
var originalObject = JSON.parse(objectData );
alert(originalObject.flag);
See the following fiddle: http://jsfiddle.net/FdhzU/
First things first: I'm not sure whether the information that I'm going to provide will be enough, I will happily add additional information if needed.
I'm serializing a complex structure into the JSON-Format, Field[i][0] is the "this"-reference to an object.
Firebug's Output on JSON.Stringify(myObj)
This is all fine and working as long as I keep it all JS. But now I have the requirement to serialize and send it to my backend to get the reference + computed information back.
Now how do I map back to the reference I had before? How do I bind this ref back to an Object?
This $$hash thing looks internal and proprietarish so I havent even bothered trying something like Object[$$hash] = ref or whatever.
This general idea probably seems pretty whack, but the result is returned asynchrously and I need an identifier to bind the new information back to the original object. Obviously I could just make up my own identifier for that, but I was wondering whether there's an option to solve it this way.
EDIT
The objects are created like this (likewise)
var arrayOfObj = []
arrayOfObj.push(new Object.With.SomeSettersAndGetters());
The Object has a method like
function GetRef(){
return this;
}
Which I'm using to keep a ID/Ref through my code.
Thank you!
Update
If you want to update a series of instances and make many Ajax requests, then you need to look at Ajax long polling and queueing techniques. You won't be able to preserve the reference, but regardless of what Ajax technique you use, make use of the below trick to preserve the reference.
Add long polling on top and you're good to go.
The idea is this:
Assume the server will respond in JSON format. If you need to refer to the original references, here's my two cents:
Update the exact references when the server replies. Say you have 10 instances of Something stored in an array. On a successful response, you use the methods in the Something class to update the specific instances in whatever way you want.
/**
* The array with something instances.
* #type {Array.<Something>}
*/
var instances = [];
/**
* The Ajax success function.
* #param {Event} event The event object.
*/
function ajaxSuccess(event) {
var response = event.target.getResponseText();
var actualResponse = JSON.parse(response);
for (var i = 0, len = actualResponse.length; i++) {
instances[i].setWhatever(actualResponse[i].whatever);
};
};
The above is a more procedural approach. If you want full blown OOP in JS, then you think in modular design patterns. Say you have a module that loads data into some place. Basically, everything related to that module is an instance property.
var myModule = function() {
this.whatever = 1;
};
myModule.prototype.loadMore = function() {
var request = new XMLHttpRequest(),
that = this; // store a reference to this.
request.send(); // etc
request.onreadystatechange = that.onSucess;
};
myModule.prototype.onSucess = function(event) {
var response = JSON.parse(event.target.getResponseText());
this.whatever = response.whatever;
};
var moduleInstance = new myModule();
myModule.loadMore();
// Now the scope is always preserved. The callback function will be executed in the right scope.
Let's assume on the backend side of things, you have a model class that mimics your client side JavaScript model. Say you want to update a reference inside a model that displays text. I use Scala on the backend, but look at the fields/properties and ignore the syntax.
case class Article (
title: String,// these are my DB fields for an Article.
punchline: String,
content: String,
author: String
);
// now assume the client is making a request and the server returns the JSON
// for an article. So the reply would be something like:
{"title": "Sample title", "punchline": "whatever", "content": "bla bla bla boring", "author": "Charlie Sheen"};
// when you do
var response = JSON.parse(event.target.getResponseText());
// response will become a JavaScript object with the exact same properties.
// again, my backend choice is irrelevant.
// Now assume I am inside the success function, which gets called in the same scope
// as the original object, so it refers TO THE SAME THING.
// the trick is to maintain the reference with var that = this.
// otherwise the onSuccess function will be called in global scope.
// now because it's pointing to the same object.
// I can update whatever I want.
this.title = response.title;
this.punchline = response.punchline;
this.content = response.content;
this.author = response.author;
// or I can put it all in a single variable.
this.data = response;
What you need to remember is that scope needs to be preserved. That's the trick.
When I do var that = this; I copy a reference to the model instance. The reference is remembered through higher-order, not current scope.
Then I tell the XMLHttpRequest object to call that.ajaxSuccess when it is complete. Because I used that, the ajaxSuccess function will be called in the scope of the current object. So inside the ajaxSuccess function, this will point to the original this, the same instance.
JavaScript remembers it for me it when I write var that = this;
function MySingletonClass(arg) {
this.arr = [];
if ( arguments.callee._singletonInstance )
return arguments.callee._singletonInstance;
arguments.callee._singletonInstance = this;
this.Foo = function() {
this.arr.push(arg);
// ...
}
}
var a = new MySingletonClass()
var b = MySingletonClass()
Print( a === b ); // prints: true
My requirement is i am pushing objects to an array on each load of window, but when i open the next window the state of the array is not visible.
var arr = [];
arr.push("something");
// It gets pushed.
When i open the new window, the array's length becomes zero again.
There is no way to do this with JavaScript alone. JavaScript is just the language. It doesn't have any direct link to the app, the page or even the browser. JavaScript can be used (and is used) in many other situations, such as in server-side applications and as a plugin language for desktop apps.
Of course, when JavaScript is used in the browser, you do need a way to "communicate", as it were, with the content on page. For this you can use the Document Object Model (DOM) API, which is implemented by every browser that supports JavaScript. To communicate with the browser itself you can use window and other global object. These are sometimes referred to as the Browser Object Model (although it's not an official API).
Now that we know that; is there an API that allows us to maintain state between pages? Yes, there is. In fact, there are several:
HTML5's localStorage
Cookies
Take this example, using localStorage:
// On page 1:
localStorage.setItem("message", "Hello World!");
// On page 2:
var message = localStorage.getItem("message");
if (message !== null) {
alert(message);
}
Easy, right? Unfortunately, localStorage only accepts key/value pairs. To save an array, you'll need to convert it into a string first. You could do this, for example, using JSON:
// On both pages:
var arr = localStorage.getItem("arr");
if (arr === null) {
arr = [];
} else {
arr = JSON.parse(arr);
}
function saveArr() {
localStorage.setItem("arr", JSON.stringify(arr));
}
// On page 1:
console.log(arr); // []
arr.push("Hello");
arr.push("world!");
saveArr();
// On page 2:
console.log(arr); // ["Hello", "world!"]
Keep in mind, though, that localStorage and JSON are both fairly new, so only modern browsers support them. Have a look at emulating localStorage using cookies and at JSON2.js.
For data to persist across an application, there must be a database. Javascript cannot accomplish this because it is client side only and mostly intended as a way to render user interfaces.