Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
EnTerr
Level 8

async_call() pattern idea

[in continuation of my previous musing at viewtopic.php?f=34&t=98511]

So here is what i am thinking about - writing a utility function that could be used to handle asynchronous calls in RSG, specifically because workhorses like roUrlTransfer, parseXml(), parseJson(), readAsciiFile(), roAppInfo, roChannelStore, roRegistrySection, roStreamSocket/roDatagramSocket are bared from use in the render thread. The prescription so far has been "ad hoc subclass Task node for each custom functionality, then tickle its .control to RUN and observe  a field if it's ready" (and tough luck if >1 task instances are started concurrently, so try not to get confused what's listening on).

Instead - i am thinking - we could get ourselves a somewhat more generic solution as a wrapper around a Task node that takes care of the complications and does not require a new subclassing every time a new long-lasting operation is needed. To be clear, this is not something i imply RokuCo should - rather, we developers could do in our code.

Here is conceptually what i have in mind:

sub init()
 ...
 async_call("some_fn_name", params, callback_fn, token)
 ...
end sub

function some_fn_name(params)
 ...
 return result
end function

function callback_fn(result, token)
 'handle the result, possibly guided by the token (e.g. what state we are in)'
 ...
end function

So async_call() when called would take care of allocating a Task subclass, run "some_fn_name" into it and listen for completion, then call the callback_fn with the result and the supplementary token. The app developer's part to this is to write two functions - the long-lasting "some_fn_name" worker, whose protocol only includes to get input from `input` field and store result in `output` field. The callback_fn protocol in turn will be called when the result is ready - and it can use the optional token as guidance where in the app flow we are.

Here is sample use:

sub init()
 ...
 async_call("regRead", "config", my_callback, "reg_config")
end sub

sub my_callback(result, token)
 if token = "reg_config":
   m.config = result
   async_call("fetch_URL", m.config.URL + "/getCategories", my_callback, "fetch_categories")
 elseif token = "fetch_categories":
   async_call("parse_JSON", result, my_callback, "parse_categories")
 elseif token = "parse_categories":
   m.categories = result
   ...
 else:
   ? "PANIC: unknown state", token
   STOP
 end if
end sub

In this regRead is utility function that reads from registry, fetch_URL gets a URL, and so on - all of them are simpel to write by using synchronous calls but that won't be an issue since they will be ran in a task thread. Such utility functions can be written once and kept part of developer's toolbelt. The only custom thing here is the my_callback function (which here is written to chain them together in a sequence - but potentially it could be more brached tree with multiple callback handlers).

Makes sense? Any thoughts?
Is there a deadly flaw in my concept?


PS. There is one thing that bothers me - yet it's not with my idea per se but rather with the Task behavior, see /sdkdoc/Scene Graph Threads#Task Node Changes In Firmware Version 7.2 - so basically every time an async operation is to be spawn, the "mother thread" (here the render thread) is getting plucked/robbed from any and all non-RSG-type local variables (ports, functions - everything that's not on the blessed list), is that so? That rings to me like - to borrow Einstein's expression - a "spooky action at a distance". Say some method/event handler relies on a roMessagePort from `m` -  in the meantime another handler calls async/task functionality and - poof!, the 1st one is royally unscrewed. "How is this even legal?!"
0 Kudos
2 Replies
destruk
Level 10

Re: async_call() pattern idea

Currently messageports aren't able to be cloned (per sdk)
https://sdkdocs.roku.com/display/sdkdoc ... ph+Threads

An interesting idea you have here.  I did try using a variable to pass along in the callback for an observer and it fails - I would think this ought to work but it doesn't.
ie -

m.b1=m.searchscreen.getChild(1) 'reference to a button that has focus
m.b1.id="search"
m.b1.text="Search"
m.b1.showFocusFootprint=FALSE
m.b1.translation=[164,670]
m.b1.observeField("buttonSelected","ButtonHandler(m.b1.id)")
m.b1.SetFocus(TRUE)

Function ButtonHandler(Name As String)
m.b1.unobserveField("buttonSelected")
Print Name
End Function

------------------------------------------------------

If I change it to 

m.b1=m.searchscreen.getChild(1) 'reference to a button that has focus
m.b1.id="search"
m.b1.text="Search"
m.b1.showFocusFootprint=FALSE
m.b1.translation=[164,670]
m.b1.observeField("buttonSelected","ButtonHandler")
m.b1.SetFocus(TRUE)

Function ButtonHandler()
m.b1.unobserveField("buttonSelected")
Print "Button Selected"
End Function


Then the callback works.  But still, it would be nice to be able to minimize extra code, enhance program flow, and use one callback function for anything needing to be done IMO.
0 Kudos
Roku Employee
Roku Employee

Re: async_call() pattern idea

"destruk" wrote:
I did try using a variable to pass along in the callback for an observer and it fails - I would think this ought to work but it doesn't.
...
m.b1.observeField("buttonSelected","ButtonHandler(m.b1.id)")

Yeah... but no. Kudos for the thinking and trying it - but that's not how observeField works, it does not take code to `eval()` as the second argument, just a name of function to late-bind by name.

I's an interesting idea though, say allow to pass there say anonymous function instead of string...
0 Kudos