Flotype / now

NowJS makes it easy to build real-time web apps using JavaScript
http://www.nowjs.com
MIT License
1.91k stars 175 forks source link

Custom event for when the client is ready (on("connect") sometimes fires before everything synced) #101

Closed mikl closed 13 years ago

mikl commented 13 years ago

It would be very helpful if there was a pattern for the client signalling “ready to go” or something like that. I am building an app that suffers from sporadic runtime errors due to on("connect") firing before the data is synced from the client.

On the client side, I have:

jQuery(function($) {
  now.user = Drupal.settings.lolQueue.user;
});

And on the server side, I have:

everyone.on("connect", function(){
  // Prepare a stripped down copy of the user object to share with other clients.
  var localUser = {
    uid: this.now.user.uid,
    name: this.now.user.name
  };
});

Sometimes, this code crashes the server due to this.now.user being undefined. An option here is to just do a setTimeout and check again half a second later, but that is inelegant and impractical.

ericz commented 13 years ago

Hi mikl,

I would suggest calling a server function from the client in a now.ready(function(){ }) block. Variable changes outside of now.ready (like your use of now.user) are synced upon server connection being established, and thus calling a server function in the now.ready block (which runs after connection is fully established should guarantee that the function is called when client data has synced

AD7six commented 13 years ago

I've found the connect handler to be not so useful as it's not possible to set anything to now before the hander is triggered.

Similarly, I get problems with client-side code like this:

now.something = "x";
now.proof();

and server code like this:

everyone.now.proof() { console.log(this.now.something) }

it prints undefined - even if inside now.ready.

with client side code like so:

now.something = "x";
setTimeout(function() { now.proof(); }, 1000);

It works as you'd expect.

This may be a more pronounced problem with increased network latency - for example in local development these problems may not materialize at all - and then you deploy and things intermittently don't work correctly - which is my experience.

It would seem that now should not process/not send method calls if it can determine it is not in a synced state - or some other such solution whereby users can modify the now object on the client and expect their next call to guarentee their modifications are present on the server now object by the time the code is executed.

dvv commented 13 years ago

Right. Assigning to a now's key looks like a sync operation, but it isn't. And setter syntax doesn't allow to use simple continuation style.

Below is how I'd personally like to see it: now.set({something: 'x', whatever: {deep: {level: {you: 'like'}}}}, function() { console.log('something is set'); now.proof(); }). We'd have then to decide should now#set be a local predefined function, or instead a real remote caller (which obeys fully NowJS way).

Setters being given up, we can remove dependencies and support whatever browsers socket.io support w/o any additional shim.

update: plus, now#set would allow to batch changes, which is virtue

mikl commented 13 years ago

@dvv: What makes it worse is that the function calls are (somewhat) synchronous, so the statements might actually take place in reverse order as in @AD7six’ example.

AD7six commented 13 years ago

@dvv for syntax I'd prefer something polymorphic allowing any of

 now.set("key", "value", cb);
 now.set({"key": "value"}, cb);
 now.set({"key": "value", "nested.key.no.wallop": "value"}, cb);

Perhaps if exposed the current setter logic could be kept allowing users to choose. now.key = "val" is pretty convenient, just seemingly not always appropriate

dvv commented 13 years ago

Well, I guess now.key = "val" could compile down to now.set({key: "val"}) which should be equivalent to now.set({key: "val"}, undefined). This'd normalize things.

On other forms of call you showed: the first seems acceptable, the second is not, as it respawns the long-beared problem of people treating dots as delimiters. Dots are perfectly valid chars in object keys. Dot. ;)

AD7six commented 13 years ago

@dvv it was in part to point out that using nested dictionaries creates ambiguity. I.e. Does whatever get merged or overwritten? how do you achieve both overwrite and merge with that syntax?

dvv commented 13 years ago

I would also introduce a deleting sugar: now.set({foo:{bar:null,baz:undefined}}) should delete now.foo.bar and now.foo.baz. Thus, the complete control of extending/shrinking NowJS context might be achieved with one simple function.

dvv commented 13 years ago

@AD7six: it always merges. If you want to overwrite a whole object, you should nullify it first. This is virtue since allows to free references and let gc turn its chainsaw on.

If it doesn't fit, it's easy to implement additional custom merge helpers. I'm sure you understand those are for advanced usage and users, which are supposed to implement them.

update: extended version now.set([{foo: null}, {foo:{bar:'baz'}}], cb) can be used to batch the changes, which are to be applied sequentially

ericz commented 13 years ago

@AD7six

So the issue you mentioned occurs because of the lag time between new values being recognized and being. If now.something had previously been defined and you merely change now.something to a new value, it would be synced to the server before now.proof is called. This is simply a limitation of getters/setters in ECMAScript.

How about this solution: What if upon a remote function call, if some nominal amount of time has passed since the last operation, run the loop that finds new properties and thus syncing them before the function call is sent down the pipe. This way you won't encounter weird issues of lines not appearing synchronous.

ericz commented 13 years ago

@dvv, @AD7six, @mikl,

In the latest 0.7 on('connect') and ready this issue has been completely resolved.

There is no longer the lag time between client side variable changes and variable syncing .

Everything is fixed =]

mikl commented 13 years ago

@ericz awesome, thanks :)