Solving delayed operations (Timer, WebSession)

I am trying to optimize my code flow. I’ve tried to use a Timer with a Period of zero in order to run some code once at the next event loop, just like I’d do in a Desktop project. The problem, however, is that during the Timer.Action even, the Session object is not set, breaking my intended operations.

In more detail: Basically, here’s the goal: A WebControlWrapper class of mine has lots of functions that modify the UI state. Instead of each call forcing an update to the rendered HTML, I’d like to do that once, when all functions have been called. With a Desktop App, I’d use a Timer for that, and do the re-rendering of the HTML once. The slight delay is not an issue, and in fact make the program more snappy because it does only one redraw instead of dozen redundant ones.

But the problem I have now is that the redraw function can’t access the Session because it’s nil when called from the Timer’s Action event.

And I cannot simply set the Session from the Timer.Action event either, it seems. I could make the Session object available via my own property/function to my wrapper code, but I wonder if that’s safe.

How do others solve this?

First of all, use a WebTimer or a WebThread instead of Timer. These two are specifically tied to the sessions they were created in. Keep in mind that WebTimers actually run in the browser, so there is some latency there.

It’s also worth pointing out that when code runs in a web app, all of the method calls resulting from the current event are executed all at once though.

If you decide that you must use raw Timers, you should look into WebSessionContext. That lets you set the current session but should be used sparingly because Session context changes can happen at loop boundaries, just like threads.

Greg, you mean all the ExecuteJavascript calls, right?

I noticed that, and that’s where I saw that it did have lots of repeating statements of setting some elements. And that’s what I’m trying to avoid. Is there a way to filter that JS code before it gets sent to the frontend, somehow?

That’s what I was afraid of. So I’d have to pass the Session context as a parameter and can’t just store that in a global variable (for the time of the Timer event) for all my code to access, if I’d stay with using a regular Timer, right?

I’ve tried using WebTimer, however:

Since my code is inside a WebControlWrapper, I cannot simply add the WebTimer control to the Page, because the wrapper control is not a page. So I tried adding the WebTimer dynamically to my control. But that doesn’t work: The Timer never fires its Action event.

@Greg O’Lone:

How do I attach a WebTimer to a Page from within a WebControl?

Actually you can pass just the session.identifier and then ask the web application class to give it back to you.

[quote=436314:@Thomas Tempelmann]Greg, you mean all the ExecuteJavascript calls, right?

I noticed that, and that’s where I saw that it did have lots of repeating statements of setting some elements. And that’s what I’m trying to avoid. Is there a way to filter that JS code before it gets sent to the frontend, somehow?[/quote]
There is no way to adjust the JavaScript that gets sent to the browser.

My suggestion would be to change your properties to computed properties and only call executejavascript if your property is actually changing.

[quote=436407:@Greg O’Lone]There is no way to adjust the JavaScript that gets sent to the browser.

My suggestion would be to change your properties to computed properties and only call executejavascript if your property is actually changing.[/quote]
Yeah, well, this is a listbox that supports hierarchical lists, and the changes are all over the place. So, when I add rows and then expand into more rows, this leads to a lot of repetitive actual html renderings. I really need to delay the rendering or I need to change the behavior in a way where the user of the listbox has to say when he’s finished adding data. Either way, it sucks.

Agreed. Not that it matters to you right now, but we are introducing the concept of “dirty” controls in the new web framework SDK and there will be an event that means “update your control now”.

I had a similar problem and solved it the following way:

  • add a boolean property RedrawQueued
  • add a method QueueRedraw that does nothing if the RedrawQueued property is set, or does a ExecuteJavaScript("Xojo.triggerServerEvent('" + ControlID + "','Redraw',[]);") and sets the property
  • add a method Redraw that sends a full update of the HTML to the control and resets the RedrawQueued property
  • let the ExecuteEvent event listen to Redraw events and call the Redraw method
  • let each content modification method call the QueueRedraw method

This requires a roundtrip to the client but avoids all the unnecessary data transfers and redraws caused by multiple calls to the content modification methods per transaction.

Tobias, smart idea. I wonder if the WebTimer works similarly.