Problems with TWebContinuousScroll and await/async

Hello,
in our project we use the TMS Web Core component TWebContinuousScroll.

We use it to fetch data from our REST server in the OnFetchNextPage event and insert this data into the scroll list in the OnGetData event.

To make sure all the data has arrived before OnGetData is called, we use an Async/Await combination to fetch the data.

This Async/Await combination works perfectly fine in many other places within our software, except in the OnFetchNextPage and/or OnGetData event handlers of the TWebContinuousScroll component.

If we use it there, TAwait.Exec does not work.
Case 1:

procedure xxxFetchNextPage(...);
begin
AURL := '';
end;

procedure xxxOnGetData(...);
begin
if TAwait.Exec(DataModel.MachineAlarmList.LoadAsync(APageNumber, APageSize)) then
begin
// Read data from data model
MachineAlarms :=
TJSJSON.parseObject(TJSJSON.stringify(DataModel.MachineAlarmList.
MachineAlarms)).Properties['FItems'];
...
end;

Theoretically the data should be fetched first and then read out. In reality the data is fetched by LoadAsync and just before it is copied there into the data model, the line TJSJSON.parseObject (...) is already executed.

Unfortunately, however, at this point the data is not yet in the data model.

Case 2:
We move LoadAsync to xxxNextPage and leave parseObject in xxxGetData.
The problem remains.

Case 3:
We move LoadAsync to xxxGetData and leave parseObject in xxxGetData.
The problem remains.

Troubleshooting:
We debugged all the described sequences in the browser and found that Asysnc/Await is always interrupted when nested events are processed.

OnFetchNextPage calls internally OnGetData, OnGetData calls further OnXXX. And at the second nesting level, Async/Await no longer works.

Temporary solution:
If you look at the source code of TWebContinuousScroll, at some point you will come across the procedure ProcessResponseData .

This is ALWAYS called after all OnXXX handlers are processed and all data is available.

Unfortunately ProcessResponseData is protected, i.e. you can't use it "from outside".

After we've moved the function "ProcessResponseData" into the public area, the following source code was created, which now works properly:

procedure xxxFetchNextPage(...);
begin
// Get Data from REST Server and store it in data model …
if TAwait.Exec(DataModel.MachineAlarmList.LoadAsync(APageNumber, APageSize))
then begin

// Read data from data model and store it in local variable
MachineAlarms := 
TJSJSON.parseObject(TJSJSON.stringify(DataModel.MachineAlarmList.
                    MachineAlarms)).Properties['FItems'];

// Move data from local variable to WebContiniousScroll Component
cscMachineAlarms.ProcessResponseData(TJSJSON.stringify(MachineAlarms));
end;

procedure xxxOnGetData(...);
begin
// not used
end;

As you can see, all data transmission is handled in one single event handler, and et voila ... everything works fine.

Question/Request
Would it be possible to make the function "ProcessResponseData" public or provide another equivalent function , so that we can push data into the component even without cascaded OnXXXEvent calls ?

Many thanks in advance for your valuable support ...
Gerhard

Hi,

You've already debugged/investigated the source code so you are aware that events are called after each other. Even though you are using async/await, that won't stop the rest of the code from executing further including everything that comes after the events are called. In JavaScript something like this sums up the behavior:

function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  const result = await resolveAfter2Seconds();
  console.log(result);
}

console.log('hello');
asyncCall(); //consider this the OnGetData event in your case
console.log('world');

==== output:
hello
world
resolved

You can wait for the data to arrive but it the code from TWebContinousScroll had already executed further and you are too late to assign the array.

What happens in TWebContinousScroll is actually more or less the same thing you are trying to simulate: It makes an HTTP GET request based on a URL (from OnFetchNextPage) and the result of that request (a JSON array) as a string is ran through ProcessResponseData.

If in your case it's mandatory to use a different component to fetch the data AND need to fetch the data when the next page should be fetched based on the behavior of TWebContinousScroll, then indeed you'll need to use ProcessResponseData or an equivalent of that to be able to show the results.

We'll consider exposing this method or an equivalent. In the meantime, you can try to do something like this instead of modifying the source code:

implementation

type
  TWebContinousScrollOpen = class(TWebContinuousScroll);

procedure xxxFetchNextPage(...);
begin
  // Get Data from REST Server and store it in data model …
  if TAwait.Exec(DataModel.MachineAlarmList.LoadAsync(APageNumber, APageSize)) then 
  begin
  // Read data from data model and store it in local variable
  MachineAlarms := TJSJSON.parseObject(TJSJSON.stringify(DataModel.MachineAlarmList.
                    MachineAlarms)).Properties['FItems'];
  // Move data from local variable to WebContiniousScroll Component
  TWebContinousScrollOpen(cscMachineAlarms).ProcessResponseData(TJSJSON.stringify(MachineAlarms));
end;

Hi Tünde,

thanks for your incredible fast answer and thanks for the hint on how to avoid modifying your source code. Now everything works fine but of cause i will change to the final solution when it will be available.

Gerhard

1 Like

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