Refactoring in 'tm-http-request' Callbacks

Good day, all.

I am requesting some information from an API that first I must request an initial set of record IDs that I then must request further details on that specific record. I have this setup at the moment with a single button click and everything works as expected. The problem is that if I want to run this as a single-click function I need to nest procedures and attempts to refactor similar code-blocks that could be used elsewhere doesn’t seem to parse correctly. I think this is because of the order of parsing operations and the scope when the callback is assigned to the ‘oncompletion’ attribute.

This is an overly-simplistic pseudocode example.

\procedure this-will-never-execute()
<$action-setfield .... />
\end this-will-never-execute

\procedure get-api-ids()
\procedure refactor-me()
    <$action-setfield .... />
\end refactor-me

\procedure my-callback()
    <<refactor-me>>
    <<this-will-never-execute>>
\procedure my-callback

<$action-sendmessage 
    $message="tm-http-request"
    ....
    oncompletion=<<my-callback>> />
\end get-apt-ids

\procedure get-api-details()
\procedure refactor-me()
    <$action-setfield .... />
\end refactor-me

\procedure my-callback()
    <<refactor-me>>
    <<this-will-never-execute>>
\procedure my-callback

<$action-sendmessage 
    $message="tm-http-request"
    ....
    oncompletion=<<my-callback>> />
\end get-api-details

<$button actions=<<get-api-ids>> />Get IDs</$button>
<$button actions=<<get-api-details>> />Get Details</$button>

I do not seem able to pull the refactor-me procedure from outside the get-api-ids and get-api-details procedure scopes and avoid redundant code. Any attempt with transclusion shorthands or the widget just result in it being interpreted as plan text and never executing.

Is this something that can be done? Is there a technique of chaining these procedures on callbacks together or some other technique that I am overlooking?

Thanks for you help!

IMO it needs to be \end refactor-me without the braces. Did you try that?

Oh that is just a mistake on my part in my example code; this is just code I wrote off-hand to illustrate and isn’t from my actual code. Everything works great otherwise, just want to avoid having duplicate code that I must manage in the current layout.

I updated my example to better illustrate what will and won’t execute, plus it is a bit closer my my actual code.

I haven’t done much with tm-http-request, and I’m not set up to test this right now. But is there a reason these need to be nested? What happens if those inner procedures are brought up a level like this?

\procedure this-will-never-execute() <$action-setfield .... />
\procedure refactor-me() <$action-setfield .... />
\procedure my-callback() <<refactor-me>> <<this-will-never-execute>>

\procedure get-api-ids()
  <$action-sendmessage $message="tm-https-request" .... oncompletion=<<my-callback>> />
\end get-apt-ids

\procedure get-api-details()
  <$action-sendmessage $message="tm-https-request" .... oncompletion=<<my-callback>> />
\end get-api-details

<$button actions=<<get-api-ids>> />Get IDs</$button>
<$button actions=<<get-api-details>> />Get Details</$button>

I’m not sure whether this is another pseudo-code typo not present in your actual code, but do note that the message is tm-http-request, without the s.

It is a typo. Thanks.

It doesn’t work. I think it is because I think the callback/oncompletion is explicitly a string so it doesn’t get rendered via wiki parser. I think this is it work and MY understanding of how those action callbacks work. But I was wanting another person verify this.

You can see this in this tiddler. It does not spawn the trival tiddlers through the action widgets.
https://the-junkyard.tiddlyhost.com/#Callback%20Composition%20Doesn’t%20Work

Looking at your pseudocode, it’s completely fine (typos willing).

What does $action-log tell you at the points I placed my comments?

Yeah, typos aside, everything works as I expected. I am just being fussy about making my code composition more proper.

Adding the actionlog widget just returns the variables I expect from the tm-http-request action. But the other procedures do not run. If you open the dev tools the log for the request shows oncompletion with: oncompletion: "<$action-log />\n<<test1>>\n<$action-log />\n<<test2>>\n<$action-log />".

My interpretation is that the parser only goes one level deep when parsing the procedure call for oncompletetion and thus returns the plain text string above. Any further parsing of procedures just will not be done and therefore <<test1>> and <<test2>> are just seen as text strings.

This one-level transclusion seems to hold true even if I use the transclude widget.

In hindsight due my below post, I think I am wrong in the above quoted text. I think it is a scoping issues. I think once the handler is called from oncompletion that all variables and thus all procedures fall out of scope, which may actually be reflected in the $action-log showing only the passed variables. If they are not explicitly passed to the handler any variable thereafter is rendered to a null.

I am hoping that someone will prove me wrong and I am just being dense, but this is not a show stopper for me. I will just keep my nested functions shallow, and my usecase is mostly triggered by user interactions.

So after setting it aside for a bit, and then coming back to it and reading the documentation a bit more I reasoned through a way to make ‘action strings’, callback, oncompletions(whatever you want to call it) to be a bit more compsition-like.

At least when calling ‘tm-http-request’ you can pass data to the handler via the var-* attribute. This is where I can pass my procedures to the handler and it can then correctly resolve its contained variables and execute the contained code.

\procedure thisworks()
<$action-setfield .... />
\end this-works

\procedure callback()
<<thisworks>>
\end callback

\procedure button-action()
<$action-sendmessage 
    $message="tm-http-request"
    ....
    oncompletion=<<callback>>
    var-thisworks=<<thisworks>> />
\end button-action

<$button action=<<button-action>> >Push Me</$button>

https://the-junkyard.tiddlyhost.com/#Callback%20Composition%20That%20Works

A nice benefit of this is this actually kind of creates a generic or interface where we could pass any procedure to the callback and change its side-effects drastically depending on the use case. Perhaps this is already well-known and actually how some aspects of TW5 is used but I thought it was unique.

I think it is a scoping issues. I think once the handler is called by the oncompletion, all variables and thus all procedures fall out of scope, which may actually be reflected in an $action-log widget the handler’s body showing only the passed variables. If a variable is not explicitly passed to the handler then that variable is rendered to a null or empty string.

I seriously doubt that is the case. That would imply there is special transclusion code parsing and execution just for tm-http-request. Nope. Don’t buy that, sorry.

I can’t check my code right now (where this actually works fine). If no one turns up with a solution for you, I’ll take a look tomorrow.

Sorry, I updated my reply. I think it is a variable scope issue and why only the variables that are passed back from the message appear in the log. So all variables that are not explicitly passed back resolve to empty strings. I just need to pass my desired variables using the var-* mechanism to allow me to build my procedure compositions.

This is exactly correct. When tm-http-request is processed by the TWcore, it has to asynchronously wait for responses from the remote server before it can trigger the callback handlers (onprogress and oncompletion). As a result, any variables or procedures needed by those callback handlers are no longer in scope when the callbacks are invoked.

To address this, the tm-http-request message uses the var-* parameters, which are typically some variables with values, but can also include whatever procedure definitions (which are, internally, the same as variables) that are to be used by the callback handlers.

Another method of achieving this is to declare those procedure definitions in a tiddler (e.g., “SomeTiddler”) that is imported by the callback handlers using either \import SomeTiddler or <$importvariables filter="SomeTiddler"> syntax.

-e

2 Likes

Ah, yes! This was the was the bit of knowledge I knew I was overlooking. It seemed like such a obviously common pattern that it had to have been addressed, I just didn’t know exactly what I was looking for: \import in the scope of the \procedure.

Thank you, Eric.

1 Like