gun
gun copied to clipboard
Gun.on(cb,true) on multiple levels not working as expected
I need Gun.on(cb,true) to give me only the changed data ( hence the 'true'). This is working fine on one level. gun.get('livingroom').on( cb , true)
. It fails when doing gun.get('livingroom').get('lights').on( cb , true)
. It will 'trigger' once with only the changed value, but a second time with the full object.
localStorage.clear();
var gun = Gun();
var root = gun.get('root');
console.log('ONE LEVEL');
root.on(function(data, key){
console.log(data)
},true);
console.log('expect : test:1');
root.put({test:1})
console.log('expect : test:2')
root.put({test:2})
console.log('expect : msg:add')
root.put({msg:'add'})
console.log('expect : test:3')
root.put({test:3})
console.log('expect : msg:add again')
root.put({msg:'add again'})
console.log('\nTWO LEVELS')
var test2 = gun.get('lvl1').get('tests')
test2.on(function(data, key){
console.log(data)
},true);
console.log('expect : test:1')
test2.put({test:1})
console.log('expect : test:2')
test2.put({test:2})
console.log('expect : msg:add')
test2.put({msg:'add'})
console.log('expect : test:3')
test2.put({test:3})
console.log('expect : msg:add again')
test2.put({msg:'add again'})
Have you tried var test2 = gun.path('lvl1').get('tests')
?
@dfreire well, i use path()
myself, but it is not loaded by default...besides that, i don't think that using path()
on a single prop makes any difference since all path()
is doing is .get().get().get() etc
Oh...and you can never use path()
at the beginning
That's interesting, because:
var test2 = gun.get('lvl1').get('tests')
has that problem
var test2 = gun.get('lvl1').path('tests')
has that problem
var test2 = gun.path('lvl1').get('tests')
seems to work
If path()
is just a convenience wrapper around get()
why can't it be used in the beginning?
Thanks
Hmm.. well you might want to look at http://gun.js.org/docs/path.html
I'm seeing behavior where, in the "two levels" case, I get 3x full document upon initialization of the listener, then when I do a .get('somefield').put(123)
, I get 3 invocations, only one of which is the expected one:

however in the top-level case, the only extraneous invocation is the one upon registration:

@eallik yeah, this is pretty difficult, because there are a certain number of "cycles" that the internal graph representation has to make when data is saved, and each cycle causes its children/proxies to also be called (internally). Without this, some data doesn't get read/notified properly, which is just wrong, however with it, it is pretty hard for me to prevent duplicates.
Clearly though, especially with on(cb, true)
(which hasn't gotten much love / testing since the rewrite) this is wrong behavior, so I'll keep the issue open for everyone to see. However, don't expect this to be fixed by v1.0 :( because it is possible to write an extension that more intelligently ignores the extra calls before calling the callback by using some deep compare (however, I won't do this in core, because it is a massive performance hit). There hopefully should be otherways in core for me to deal with it... but low priority :(.
Thanks for leaving this open...I have been struggling to understand this for some time.
I ended up not using {change: true}
on on()
and instead I flatten my object into a single level object of key: value pairs, where the key is the 'path', eg, for the following object hierarchy:
{
'0': {
'id': 'a',
'type': 'node',
'children': {
'0': {
'id': 'aa',
'type': 'leaf',
'data': 'first leaf:aa'
},
'1': {
'id': 'ab',
'type': 'folder',
'children': {
'0': {
'id': 'aba',
'type': 'leaf',
'data': 'second leaf:aba'
}
}
}
}
},
'1': {
'id': 'b',
'type': 'leaf',
'data': 'third leaf:b'
}
}
(The object I want to 'sync' between peers is like the above) When it is flattened, it is like this:
"0.id": "a"
"0.type": "node"
"0.children.0.id": "aa"
"0.children.0.type": "leaf"
"0.children.0.data": "first leaf:aa"
"0.children.1.id": "ab"
"0.children.1.type": "folder"
"0.children.1.children.0.id": "aba"
"0.children.1.children.0.type": "leaf"
"0.children.1.children.0.data": "second leaf:aba"
"1.id": "b"
"1.type": "leaf"
"1.data": "third leaf:b"
I want a callback when a peer changes (modifies, deletes, adds) to the above structure, and I couldn't get get("testing").on(console.log, {change: true});
to make much sense. It seemed to always return the whole thing no matter what was changed.
However, I did seem to get some sense from:
get("testing").map().on((data, key) => console.log({key, data}));
That seems to create an 'on()' for each of the properties, and also create new ones whenever a new property is created.
That seems kind of inefficient (akin to placing an eventListener on each item on a <li>
compared to one on the <ol>
and the like); and I have yet to see if it has any performance problems with large objects.
I am also scratching my head that the current client's on()
handlers are invoked when the current client does a put()
...is there no way to tell Gun that I only want to know about changes made by other peers, not by this one.
This all does seem rather complicated for such a basic use-case - ie wanting to sync an existing object between peers. Is there some documentation for how to use Gun for this use-case?