Form Load Aync Loading

Kind of an odd question. I tend to have forms that are larger in size, so when loading them, there is naturally going to be a delay. However, I'm wondering if it is possible to have a loading mechanism that is a little more friendly in terms of blocking the main UI. As it is now, even though the call to load the form is async, it blocks the main UI anyway.

The idea might instead be to have the loading broken up into much smaller chunks so that it doesn't interfere as much with the UI? I don't mind if it takes a bit longer as a result. Maybe a separate "LoadNice" function or something along those lines?

To give an example, I've got a login form that loads immediately. While the user is logging in, I'd like to load up the considerably larger main form in the background, rather than having a delay of a few seconds before the login form is shown. This works, but input into the login form is blocked while the main form is loading anyway, so not really a big improvement, though being able to see the form right away is a bit of a step forward.

I'm assuming input is blocked because the load function is doing a lot of work and doesn't give the main UI a chance to do anything while it is working. I'm aware JS isn't multithreaded, so there's not much to be done aside from rewriting the function to maybe do things a little differently?

I assume you use a promise based method to show the form (and you have the code to do something with the form after waiting for the promise)?
Maybe it is an idea to not use the promise based method and handle form initialization in the callback? In this scenario, there shouldn't be anything interfering with the UI thread (assuming the code in the method with waiting for the promise is crucial for the responsiveness of the app?)

Simpler than that, even. Seems I'm back to a previous issue where loading the DFM is just something that takes time and resources and whenever that happens everything sort of pauses until it completes. Even kicking it off via a timer just means the UI pauses whenever the timer issues the request. Maybe four or five seconds in some cases. I guess I'll just have to work on cutting that DFM down quite a bit. I think you've already done some work in that area previously.

I have tried both variations from the docs - the regular callback method and the async method, and the result is largely the same. In the timer event that kicks off the form load, the async method will naturally wait for the form to be loaded, which incurs the 5s delay in the timer event itslef. Using the prior non-async method, the timer event completes and then the form creation occurs. But the UI still pauses for the same 5s while the form is created.

Some more explanation. At the end of the timer event, I do some UI updates to indicate that the form is loaded (anticipating using the aysnc method, this would be accurate). But even with the non-async method, those UI updates don't actually get displayed until after the form loads even though the timer event is done, because the form creation is such a heavy call. When it eventually runs, it still causes a UI block. It runs presumably immediately after the timer event ends but before the updates from the timer event make it to the page.

Just out of curiosity, where in the TMS WEB Core source does it actually process the DFM? I saw lots of calls to LoadDFMValues in places like WebLib.Forms.pas but after poking around for a bit I couldn't figure out where it was doing this work? Was expecting to see a loop of some kind that could maybe be broken up in some fashion, or perhaps filter the DFM ahead of time. Perhaps it is done when the project is compiled though?

In TMS WEB Core DFM files are not loaded. Part of the compiling process translates DFM files to code and to "load" a form, this code is executed.

Ah, very good. I will have to work on cutting down the size of the DFM then.

I had a look at the LoadDFMValues code that is generated. In this instance, it turns out to be a block of 7,500 consecutive function calls. That's going to take some time to work through!

I'm busy paring that down a bit, likely able to chop 20% off without too much work, but more difficult after that. So I guess back to my original question. Would it be possible/feasible to get another version of LoadDFMValues that is a little less heavy? Granted, its current implementation is likely the fastest possible, given what it is doing, but sometimes the fastest isn't the best in terms of blocking.

Perhaps between elements, or maybe every 10th element or something like that, a JavaScript equivalent of sleep could be called (several variations using setTimeout seem possible now) to let the app do other things while this is going on. I realize this is not a multi-threading environment, but perhaps it could be more closely simulated in this instance. Another idea would be to have each element grouped within a single function call, and then only call one of those during each browser refresh or some other rate-limiting mechanism.

Anyway, not a critical thing, either way, just one of those would-be-nice-to-haves. Last time I was struggling with DFMs I was asking to increase the absolute speed of LoadDFMValues, directly the opposite of this situation :smirk:

Maybe I just need smaller forms. But where's the fun in that?

Absolutely not trivial to do this in a kind of generic way.
Do you have an idea what components are being used and possibly take up the longest time to initialize? If this is related to specific components, we could investigate if there are optimizations possible or alternatively, for now, create these components in code at run-time?

Aha! A little more digging. Most everything is embedded in a TWebPageControl at some point. And I just recalled an old issue about switching pages being slow, which was addressed a few versions back. But I tried it again here - having the TWebPageControls marked as Visible := False in the IDE and then marking them as Visible := True in WebFormCreate. And voila! 5s is now less than 1s.

I guess when LoadDFMValues is working, it is updating something as it goes, and having these marked as Visible := False bypasses that and things run along much quicker, with the updating happening only when they are marked as visible again.

Much better now.

Good to hear.
The visible setting can make a difference for things like alignment / anchoring calculations, so excluding what is not necessary will help.

Hmm.. A related question, then. I started out using TMS WEB Core from a Delphi mindset, using its alignment properties, adjusting margins, fiddling with various width/height things in the same way I would in a VCL app.

But lately, I've been taking more of a CSS-first approach, setting everything to "auto" and "relative" and just using CSS to do the bulk of the layout work, passing everything via ElementClassName Bootstrap classes. This means that the Delphi-style properties like top and width are no longer in sync, nor are they really needed as much - CSS looks after everything.

I'm wondering if this means that there is some aspect of TMS WEB Core that can be bypassed - this keeping track of alignment/anchoring calculations issue? Not sure if there is benefit to bypassing it generally, but if things like visibility and positioning on the page are handled through things like flex, then the information from the Delphi side isn't accurate anyway. Just thinking out loud here.

If you set control.ElementPosition = epIgnore, the Delphi setting isn't used and only HTML,CSS will control layout. Align or anchoring won't affect the layout (and calculations) then.

1 Like

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.