amark / gun

An open source cybersecurity protocol for syncing decentralized graph data.
https://gun.eco/docs
Other
18.03k stars 1.16k forks source link

Add a function to perform an asynchronous "gun.get()" + tests #1258

Closed noctisatrae closed 2 years ago

noctisatrae commented 2 years ago

Asynchronous "gun.get()"

DISCLAIMER: this functionnality is optional meaning that I have not added it to the /src folder which bundles gun itself. The source code is instead in /lib meaning that it can be imported if the user wants.

The goal of this PR is to introduce a way to quicly query the database asynchronously. I also included a inject function to allow the user to use its Gun configuration (eg. const gun = Gun({ peers: ["https://gunpoint.herokuapp.com/gun"] }). In order to use it you must just pass it to the function and it'll be automatically used.

Example

This function allows you to write:

let data = await get(`this is a test`);
console.log(data);

/*
{
  _: {
    '#': 'this is a test',
    '>': {
      l4pqkx1ihbPFwyN: 1655910875718.001,
      l4pqkx1jHgZS44c: 1655910875719.001,
      l4pqkx1j02jUyiCK6R: 1655910875719.003,
      l4pqkx1j05JBa74d2: 1655910875719.0051,
      l4pqkx1j08cv7cc14: 1655910875719.007,
      l4pqkba002j9uQhSOn: 1655910847513,
      l4pqkba07557SmB: 1655910847512.001,
      l4pqkba101a31UNqzR: 1655910847513.002,
      l4pqkba103tKkEIL5s: 1655910847513.004,
      l4pqkba106nVTfaeAD: 1655910847513.006
    }
  },
  l4pqkx1ihbPFwyN: 'Hello, 0',
  l4pqkx1jHgZS44c: 'Hello, 1',
  l4pqkx1j02jUyiCK6R: 'Hello, 2',
  l4pqkx1j05JBa74d2: 'Hello, 3',
  l4pqkx1j08cv7cc14: 'Hello, 4',
  l4pqkba002j9uQhSOn: 'Hello, 1',
  l4pqkba07557SmB: 'Hello, 0',
  l4pqkba101a31UNqzR: 'Hello, 2',
  l4pqkba103tKkEIL5s: 'Hello, 3',
  l4pqkba106nVTfaeAD: 'Hello, 4'
}
*/

console.log(data.list());
/*
[
  'Hello, 0', 'Hello, 1',
  'Hello, 2', 'Hello, 3',
  'Hello, 4', 'Hello, 1',
  'Hello, 0', 'Hello, 2',
  'Hello, 3', 'Hello, 4'
]
*/
noctisatrae commented 2 years ago

I'll be writing the documentation if the community thinks this PR can be merged. Looking forward to read what you all think !

bmatusiak commented 2 years ago

you have a global. used and will break apps if use multiple times. you are prototyping off the Object Global class.. 'we need to stay away from that'

the core of this code is

await gun.get(what).once(data => {
    result = data;
})

and is already a standard way of using gun.

noctisatrae commented 2 years ago

Hi @bmatusiak ! Why will it break app? Also... why would it be bad to have a short-hand to quickly the database ? It is useful, isn't it ?

Thank you for your contribution, please elaborate.

bmatusiak commented 2 years ago

if a app uses multiple gun instances

my setup

const gunA = Gun({ peers:["https://gunpoint.herokuapp.com/gun"] });

inject(gunA);
let dataA = await get("this is a test");
console.log(dataA.list())

const gunV = Gun({ peers:["https://privatehost:845/gun?auth=8as76df98a7s6d"] , localStorage : false });
inject(gunV);
let dataV = await get("this is a test");

later trying to get data again


let dataA = await get("this is a test");

is now dataA is broken unless you call inject(gunA); again

noctisatrae commented 2 years ago

Ok. I see. However... isn't it a problem to use 2 instances of gun in the same file?

Also, we can work this out?

noctisatrae commented 2 years ago

Guys. I think it's still a good idea but as @bmatusiak said... maybe it is not very interesting. I'll be closing the PR for now. You can re-open it later ok?

amark commented 2 years ago

@noctisatrae no no no! I just got here to look at it (had a busy week)

I think contributions like this should be encouraged.

This is a better PR than most that I see (for instances, that try to change semicolons or tabs, or some arbitrary thing).

It is voluntary, it doesn't force anyone into using or not using something, its small & does something, it has a test, etc.

While @bmatusiak is correct maybe need to fix a thing/race condition, or whatever, that doesn't mean the PR should be closed.

There is a lot of people who like/want promises and your energy & enthusiasm at documenting that is much needed!

amark commented 2 years ago

@noctisatrae no no no! I just got here to look at it (had a busy week)

I think contributions like this should be encouraged.

This is a better PR than most that I see (for instances, that try to change semicolons or tabs, or some arbitrary thing).

It is voluntary, it doesn't force anyone into using or not using something, its small & does something, it has a test, etc.

While @bmatusiak is correct maybe need to fix a thing/race condition, or whatever, that doesn't mean the PR should be closed.

There is a lot of people who like/want promises and your energy & enthusiasm at documenting that is much needed!

noctisatrae commented 2 years ago

What's next for asynchronous gun?

I. Rethinking its implementation

The inject, if I'm being honest is a ~way~ to correct a design flaw. We could at this point either rethink it or get rid of it completely by directely putting the functions into gun chaining.

Here are a few points yet to be discussed:

Let me know if you think of something.

II. "OK. I see your point, but what is more likely to happen between this two choices?"

Gun as its core is extensible ; meaning it is predicated on modules. Wouldn't it make more sense to keep on doing that ? It is harder, will require testing, a bit of mathematics and a lot of effort to do it right. Hopefully, it'll be amazing!

bmatusiak commented 2 years ago

i have dwelled on promise logic issues,

1 idea i had was maybe we can make a chain called promise(action) like promise("on") or even a called async like await gun.get(something).async("on")

it would be easy to add this in, and i may have time to work on it, in the next few days

also there is a lib called promise.js, i only viewd the code, never used it, but looks like some extra logic is useful in that

noctisatrae commented 2 years ago

Hey @bmatusiak! Hey @amark ! Going back to you. I've been working on prototype. It's quite hard messing with gun source code because it is complex.

@bmatusiak, the idea that you proposed didn't sound appealing to me as I find that it is very verbose. Why not just do something like that:

let data = await gun.aget(<key>);
// or
let data = await gun.async("get", <key>);

However, if you really think it's the right thing then... let's go! But I wanted something more... elegant.

EDIT: could someone explain to me how gun.get() and gun.get().once work? I'd like to refactor/adapt/reproduce the code.

bmatusiak commented 2 years ago

on and once are emitters,
on will fire every time it changes and give you a stream of data from other peers, once will grab the current value, and only fires one time

bmatusiak commented 2 years ago

and the reason why i think promise(action) is a good direction is because it sets us up for future and current chains to wrap anything in a promise

if later someone creates fun chain then we can do await promise("fun")

noctisatrae commented 2 years ago

I just read what you've written and I think it's very cool to develop the feature to be not only useful but also allowing extensibility by making it flexible !

When I was prototyping I came up with something that could maybe help us do what you said:

// Imagine it's the gun chain here.
let gun = {
    some_func: () => {
        // do stuff here    
    }

    // some_func2, some_func3...
}

const exec_async = async (what) => {
    // return asynchronously the value of the executed function!
    return await gun[what]();
}

// later...
await exec_async("on");

Is it what you thought of?