How delay an action?

I execute some command that imports a JSON structure into a tiddler.
I now wish to split up this blob into its parts.

I succeed to do this - if I do them separately. The problem is (or at least seems to be) that the splitting up starts before the importing is finished, so the imported tiddler doesn’t yet exist. Hence no splitting. If I execute the splitting on the tiddler from the previous import, the splitting works.

The import is executed via a button. I’ve tested the utilizing the innate order in which (I think) buttons execute things, i.e:

<$button
	set/setTo=executedthird
	actions=executedfourth
>
executedfirst
executedsecond
</$button>

but this is not enough, I guess the import is just too slow (even though it really appears very fast to me).

I obviously don’t want to manually execute the two things separately, so what can be done to delay that second action until the first is actually finished (and the resulting tiddler from that action exists)?

Thank you.

Hi @twMat,

Note that the example I gave you for using tm-http-request saves the data retrieved into a tiddler, but you do not need to do so. The variable data holds the JSON retrieved, just work with that variable directly and create the tiddlers from the JSON.

For other situations where you might run into timing issues with actions, looking at https://tiddlywiki.com/#ActionWidget%20Execution%20Modes

1 Like

Aha!!! I didn’t yet try it but, still, can I be sure the fetching is finished (and the variable has a value) before the other action is executed?

It is not the full answer but you can force a pause by placing and Action confirm widget between the two parts.

This has being discussed in the past, have you tried moving different actions to a macro and using the actions= parameter on the button ?

  • There may be a chance to leave one set of actions inside the button, perhaps in your example the contents of executedfourth may be done first?
  • Remember that things inside the button are “evaluated” when generating the button, where the actions= macro is evaluated at run time (I think)

Yes.

Note how we provide a oncompletion attribute for the tm-http-request. These are actions executed after the request has completed.

\procedure completion()
\procedure errorHandler()
<$action-setfield $tiddler="$:/temp/fetcherror" tags="$:/tags/Alert" text=`There was an error fetching the URL: $(error)$, statusText: $(statusText)$, status: $(status)$`/>
\end errorHandler
<$list filter="[<status>match[200]]" emptyMessage=<<errorHandler>>>
<$action-setfield $tiddler="data" text=<<data>>/>
<!-- instead of saving the JSON, just work with it from the variable and create tiddlers -->
</$list>
\end completion

\procedure fetch-url()
	<$action-sendmessage
		$message="tm-http-request"
		url="https://saq-twlinks.tiddlyhost.com/tiddlers.json"
		method="GET"
		oncompletion=<<completion>>
		onprogress=<<progress>>
	/>
\end

<$button actions=<<fetch-url>>>get json data from url</$button>
2 Likes

Actually, what is the scope where the data variable is accessible? Is it perhaps only within the onprogress call?

No, the variable is made available to the actions executed on completion, i.e. the oncompletion attribute. You can however, easily transclude other tiddlers or procedures in there if you want to break up the code into smaller pieces.

1 Like

I appreciate your help @saqimtiaz but it just doesn’t work.

I’ve cleaned up this wiki to make it somewhat friendly for inspection. It reuses a UI from an overwritten plugin that used to work for importing from google sheets (hence the shadow titles). The tiddlers of concern are these two at river top:

  • $:/.../SheetSelector
    contains the http-request. The completion procedure in it does not even seem to call the TEST-IF-DATA procedure. (In reality the TEST-IF-DATA procedure will be exchanged for a procedure splitting up of the data.)

  • $:/.../readme_demo
    has the IMPORT button. The selectwidget etc is disabled, so only the IMPORT button is to be used.

Any help would be appreciated.

What specifically does not work? I can see that the JSON is fetched and save in the tiddler data.

Firstly, the list widget tests whether the HTTP requests completed successfully, calling the error handler if it did not. So any further handling should happen within that list widget.

Secondly, you are in the world of asynchronous operations now. The HTTP request oncompletion actions take place in a new context that does not have any of the other variables available. To work around this study the two examples you have, which illustrate two of the three possible ways to handle this:

  1. import macros and other globals at the beginning of the completion procedure, or
  2. pass the variables you need in the oncompletion handler as attributes to the tm-http-request message (see docs for details), or
  3. nest the extra procedures you need inside the completion procedure.

This is somewhat of a tangent though, the most direct way to make progress would be to do the JSON handling and creation of tiddlers inside the list widget in the completion procedure.

Much appreciated!

As you state, directly putting my test to create another tiddler (beyond the creatino of the data tiddler) inside that listwidget does work - great! But putting a procedurecall there instead does not seem to advance the data variable. It even doesn’t work if I put a full transclude procedurecall with a parameter data=<<data>>. I’ve updated the wiki to reflect these attempts. Why do TEST-IF-DATA-1 and TEST-IF-DATA-2 not work?

The reason for wanting external procedures outside of the oncompletion procedure is for readability, i.e the “tiddlerization” of the JSON string is a rather big procedure so I figure it better if presented separately.

I addressed this in my previous reply and outlined the options for achieving your goal.

One way to think of this is that the HTTP request is asynchronous and will complete at some unknown time in the future. As such, the actions that are executed when they complete have to be independent of the local scope in which the request was initiated. The tiddler from which the request was initiated may have been closed from the story or deleted by the time the request completes, or the values of variables may have changed in the interim. As such, the completion actions have no knowledge of the variables that exist when initiating the request unless you explicitly make them available. In the context in which those actions are invoked, TEST-IF-DATA-1 and TEST-IF-DATA-2 do not exist.

I faced the same problem and used this within $button action to process and update tiddlers in sequential stages:

<$button>
	<$list filter="1 2 3" variable="ProcessStage">  
		<$list filter="[<ProcessStage>match[1]]" variable=none >
		...
		</$list>
		<$list filter="[<ProcessStage>match[2]]" variable=none >
		...
		</$list>
		<$list filter="[<ProcessStage>match[3]]" variable=none >
		...
		</$list>
	</$list>
EXECUTE</$button>

Works for my purpose :slight_smile:

1 Like

@jacng Thats a very interesting solution. It won’t work in my specific case (because, as I’ve learnt, the http-request mechansim has special demands) - but, regardless, it raises an interesting question about the listwidget:

{{{ [thisRunTakesOneHOURtoProcess] [thisRunTakesOneSECONDtoProcess] }}}

Will the listwidget leave the second run untouched until it is finished processing the first one? I expect this because the second might depend on the first, but I’m not sure.

You are right, of course. Your’s is a different context from what I encountered.

Will the listwidget leave the second run untouched until it is finished processing the first one? I expect this because the second might depend on the first, but I’m not sure.

Now that’s interesting, for me! I have not actually seen a run or filter expression that directly execute actions within itself, but a run or filter expression can include macros with actions and they will execute, as long as the run or expression arises from some button action :bulb:

It does seems that the second run will wait for the first run to finish, and also my list filter processing can all go into sequential runs instead… mmm that’s something to explore.

Yes. Theoretically that would work. But the browser will show you a message, that one of your tabs does not respond. It will ask you to “stop” or “wait”. While this message is shown the tab will be suspended. So it will be stopped already.

If you click “wait”

The tab that is waiting will be unresponsive. So no user input can happen while “thisRunTakesOneHOURtoProcess” is active. …

So it will not work that way. Except you will want to wait 1 hour and do nothing.

1 Like