Idea: make async easier with "Then" event

  • I was thinking about the new Local Storage plugin and how it's a little tricker to use with the new asynchronous style of access. Previously with WebStorage you could just directly retrieve a value in an expression:

    This is really easy to use, but has the disadvantage of being synchronous, i.e. it pauses and waits for "MyValue" to be read from storage. If the system is busy or it's a large value, this could take say 100ms, and the game freezes for this time (jank).

    Local Storage solves this with asynchronous storage. You use the 'Get item' action, which then triggers 'On item get':

    Now if it takes 100ms to read, it continues running the game while reading from storage in parallel, and when finished it triggers 'On item get'. The new problem is now the reading logic is in a trigger event. This event could be far away from the action that started it (e.g. in a different sheet). It could also be inconvenient if there are multiple 'On item "MyValue" get' triggers for different purposes, and they all trigger at the same time based on this one action. This gets more complicated if you want to do something else async afterwards, e.g. set a value, which involves more triggers.

    This can be solved with good organisation, but it would be nice if we could have events that are both async and as easy as the sync version. In Javascript "Promises" go a long way to help making this easy, so I was thinking of a similar type of feature for the event system - a "Then" condition, which triggers whenever the previous event's asynchronous action finishes (mockup):

    The "Then" trigger is tightly coupled with the previous event, in the same way the "Else" event is based on the event directly preceding it rather than working by itself (and that also means there are valid and invalid places to put it). It works the same as the previous async example, but:

    • you didn't have to find the right trigger to use
    • you didn't have to type in "MyValue" again for the trigger, it knows it's triggering for that value from the previous event
    • it guarantees the thing to be done afterwards is in the next event, not somewhere far away, keeping events tidy
    • it only fires the one "Then" trigger, not every trigger of that kind in the event sheet, making it easier to use if the value is loaded for different reasons in different places
    • this might end up being a C3 feature, not sure it would make C2

    I think to make it clear the asynchronous action needs to be clearly marked - I just scrawled in a red asterisk as a placeholder representation.

    This kind of asynchronous "Then" event could be applied to all sorts of objects which already implement asynchronous features:

    • AJAX "Request URL" could be followed by "then" which is fired when the response is received, and would mean you don't need to worry about getting tags right
    • "Request user media", "Request fullscreen", "Request location" or other user-prompting actions can fire "then" after they are approved/get data
    • Pathfinding could fire "then" after the path is found
    • third party plugins could integrate with the "then" trigger
    • anything we add in future could more easily use asynchronous actions, making it easier to add parallel features that make use of multiple cores

    A few other thoughts:

    • the "Then" condition would have the caveat that you can only have a single async action in the previous event. If there were two, it wouldn't know which to fire "then" for. So in that case you'd probably have to go back to using the existing trigger system.
    • "Then" triggers could be chained. So the "Then" event could contain another async action, and be followed by another "Then" trigger, making a clear sequence of actions which run in parallel.
    • Async actions can fail, e.g. AJAX "On error", Pathfinding "Path not found" etc. I guess there would need to be a kind of error version of "Then", which could also come after the "Then" event. Javascript calls this "catch".
    • I'm not sure the name "Then" is the best name - it's the term Javascript uses, but it could confuse beginners who often want to figure out how to make an "If - then" type logic (which is basically just a normal event - "if conditions true, then run actions"). It would be especially confusing that "Then" is a trigger. Other trigger names could be "Next", "After", "On done"...

    A different idea I had was to make asynchronous actions like the "Wait" system action, except instead of just waiting, it would be processing some async action. I think this is limited though, since it makes it difficult to run asynchronous actions in parallel - if an event had 3 async actions, this method would force them to run one after the other in sequence, rather than starting them all at once and firing triggers as each completes (important for best performance and multi-core usage). So I think the "Then" event is a better idea.

    Does anyone have any thoughts about this? Good idea or bad idea? Perhaps there's an even better way it could be approached?

  • An additional idea raised by talking to Kyatric:

    Have another kind of "All completed" trigger for events which start multiple async actions in one event. So if you start 5 async actions in "On start of layout", and follow it with "All completed", the next event runs when the 5 async actions in "On start of layout" have all finished.

  • Was about to ask about something like that, cause new localsotage kind of breaks my workflow xD

    "Then" is a nice idea but i think using only the name "then' will confuse a lot of users. Maybe this little green right arrow could be change to something different, like arrow pointing up or whatever. Just to differentiate it better from standard events and so when you see that event you now right away why is it there and what is doing. (Like i'm using blank events instead of "every tick" - I can tell without reading that this event will run every tick)

  • Your second post was my question i had in mind .

    I had A LOT of webstorage functions in my game and i now am trying to tune them to LocalStorage.

    Somehow i do not get it working in any way. I can't even get a value to be written ( checking with debug LocalStorage shows empty ).

    Could you tell me why this isn't working:

    I use the "setup" key to set all values the very first time the game runs.

    P.s. Sorry if i am hi-jacking your topic.

  • Perhaps it should automatically appear and conjoin to an event that uses asynch so that the user can't get confused/realizes it's part of the command.

  • Jayjay, just what I was about to post. I like the idea of the then statement. However, I think that the tag system should still be in place optionally. Given the example of 5 async actions being run at the start of layout, the "On all complete" covers the basics of performing these actions. But what if you want to know when just one of the 5 has happened? I mean, you could just call that one after the first 4 as an action of the "On all complete event". But if the user wants to optimise, then the tag method would work better, where only the one action is given a tag and then picked up by a tagged 'Then' event, and the "On all complete" event fires after all 5.

  • Perhaps it should automatically appear and conjoin to an event that uses asynch so that the user can't get confused/realizes it's part of the command.

    But if i want it to fire not only after "On Space pressed"? Let's say i have multiple events with Get Item?

  • Isn't this kind of like "wait for signal"?

  • Your second post was my question i had in mind .

    I had A LOT of webstorage functions in my game and i now am trying to tune them to LocalStorage.

    Somehow i do not get it working in any way. I can't even get a value to be written ( checking with debug LocalStorage shows empty ).

    Could you tell me why this isn't working:

    I use the "setup" key to set all values the very first time the game runs.

    P.s. Sorry if i am hi-jacking your topic.

    Well, as far as I can tell, you should have an action "Check item "setup" exists" in the "Start of layout" event, that would then trigger the "On item "setup" missing" event.

    Also, the new LocalStorage is asynchronous. It means, there is a delay between the moment the action is encountered, and the action is executed.

    In your first event, you likely restart the layout before any of the values are set to be written.

    Consider them like AJAX requests, they may vary in the time and order taken for them to complete.

    So here, that's where a "All requests complete" condition like Ashley is suggesting would be nice as Event nb 2 to which the action would be "Restart Layout".

    Currently, I'd try to have a "On item "setup" set" condition/event nb 2 that would contain the restart layout action.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • But if i want it to fire not only after "On Space pressed"? Let's say i have multiple events with Get Item?

    That's where the "on all complete" trigger Ashley was talking about would come into play, but maybe the Then feature can be turned on and off in the same way that you can right-click an event and select "OR" mode.

  • Kyatric Thanks for the reply!

    I tried out some more things. Conclusion i get is that every thing has to be separated to work correctly.

    This way i need a lot of events to get the simplest results..

    Below is what i tried and it works but well.. that's a lot of events for something simple.

    Is this for now the way to go?

    P.s. here is the capx: https://www.dropbox.com/s/zp2f6bs1bkjng ... .capx?dl=0

  • I think that the "Then" idea is only useful for cases that only requires a single value check like AJAX and Pathfinder. But for those cases I think the current system with custom conditions is more descriptive of what it does.

    For storage I would prefer to process groups or batches of values at once, otherwise it would be a PITA. Like setting together the X and Y of an object from LocalStorage in a single action, instead of storing the returned values in local vars as buffers:

    Sprite: Set position to ( LocalStorage.get("X") , LocalStorage.get("Y") )[/code:gg9jmxwm]
    
    I propose adding tags to each 'get item' action, like this:
    [code:gg9jmxwm]>Keyboard: On Space pressed
    -LocalStorage: Get item "PlayerX" under tags: "SpriteAttributes", "SpritePosition"
    -LocalStorage: Get item "PlayerY" under tags: "SpriteAttributes", "SpritePosition"
    -LocalStorage: Get item "PlayerHealth" under tags: "SpriteAttributes"
    -LocalStorage: Get item "PlayerShield" under tags: "SpriteAttributes"
    -LocalStorage: Get item "PlayerWeapon" under tags: "SpriteAttributes"[/code:gg9jmxwm]
    
    Then it would be possible to test both when all the items of a specific tag are ready, or when a single item is ready:
    [code:gg9jmxwm]>LocalStorage: On all items from tag "SpriteAttributes" ready
    -Sprite: set position to ( LocalStorage.get("PlayerX") , LocalStorage.get("PlayerY") )
    -Sprite: set variable "Health" to LocalStorage.get("PlayerHealth")
    -Sprite: set variable "Shield" to LocalStorage.get("PlayerShield")
    -Sprite: set variable "Weapon" to LocalStorage.get("PlayerWeapon")
    
    LocalStorage: On all items from tag "SpritePosition" ready
    -Sprite: set position to ( LocalStorage.get("PlayerX") , LocalStorage.get("PlayerY") )
    
    LocalStorage: On item "PlayerWeapon" ready
    -Sprite: set variable "Weapon" to LocalStorage.get("PlayerWeapon")
    [/code:gg9jmxwm]
    
    In my opinion this system can be more flexible and indicates more clearly what it does.
    I also think that renaming the 'LocalStorage' plugin to just 'Storage' would make things less verbose  without compromising the understanding of what it does.
    
    *Edit
    
    I thought a bit more and while the tag system can work, it would require to think and track different tags if you want to get the same values multiple times along an event sheet. Now I see that the "Then" proposal is all about a scoping system for asynchronous events to relate only to the events that called them (without the need to explicitly name them).
    
    I always felt the need for something like this when using timers, and yes, it would be useful for all kind of asynchronous events.
    
    @Ashley
    Why would the "Then" trigger only be able to have a single async action in the previous event? Couldn't it run like an 'all completed' for the previous event async actions, and use explicit key references in it's actions like my suggestion above? I think the use of 'LocalStorage.ItemValue' instead of an explicit reference like 'LocalStorage.get("PlayerWeapon")' is what it's limiting it. If you store all the loaded values in a buffer under the hood you could explicitly reference them all.
    
    Another thought is that since this is a problem of scope representation at hearth, maybe sub-events can be used somehow for an elegant solution. I'm not sure how, but it may be a possibility consistent with how local vars scope works.
  • Ashley

    I only have one thing to say, please do not use "then" because just like you say, If..then. It would confuse me heavily if I was newcomer. (with the if..then in mind)

    "On done" feels better.

  • A "cache" logic might reduce the response time and the complex of reading action.

    For example, a cache with dictionary structure -

    User could load this dictionary cache by reading all key-value pairs into memory, then these values could be read immediately.

    The structure stored in local storage might be

    • Index: A JSON sting to store the keys
    • Content: Key - value pairs

    Plugin first read index, then read all key-value pairs. And the manipulation of this dictionary cache is just like official dictionary object.

    Writing action might be triggered after ticking (in tick2() maybe), to write key-value pairs with dirty bit = 1.

  • I agree with Rex. When I suggested a model to deal with the change for Android and to switch over to a memory management similar to AJAX triggers. I suggested a cache. I still suggest a cache.

    Since session is removed as it does seem redundant. Why not offer Cache storage as part of LocalStorage. When using cache storage there is always a running memory copy that is like a dictionary and behaves just like webstorage. Cache storage is loaded at the beginning of the app and regularly writes to storage location. So now you get the benefit of the immediate old style, but deals with work around of using the DB styles. This then means you don't need "Then" as a follow up.

    Also add save frequency based on how often the cache should save. A sample would be "Data Change, LayoutEnd, Once a Minute, Never". Then also have an option to "save cache".

    So now you get the best of both worlds. high memory data should not be in cache. where as small memory data would be.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)