Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / pas2js / 2020-08

TDataset load local JSON in a async way. How?

6 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

warleyalex

In this example:
https://pas2js.github.io/master/ProjectFishfacts/index.html

I use TDataset which inherits from TBaseJSONDataSet to load a local JSON file.

If you see at
https://github.com/pas2js/pas2js.github.io/blob/master/master/ProjectFishfacts/ucds.pas

It is possible to load synchronously but there is that warning: [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
--> FReq.open('GET',FURI, FALSE); // async = false

but would prefer TDataset to load asynchronously using the modern (async/ await / fetch). But unfortunately, I could not find a way.

function fetchAsync(url: String): TJSPromise; {$ifndef
InLazIDE}async;{$endif}
var
  response: TJSResponse;
  data: TJSPromise;
begin
  response := await(window.fetch( url ));
  Result := await(response.json());
end;

Any idea how to TDataset load in a async way?

Reply by email

Michael Van Canneyt
to warleyalex
warleyalex wrote 17 lines
In this example: https://pas2js.github.io/master/ProjectFishfacts/index.html I use TDataset which inherits from TBaseJSONDataSet to load a local JSON file. If you see at https://github.com/pas2js/pas2js.github.io/blob/master/master/ProjectFishfacts/ucds.pas It is possible to load synchronously but there is that warning: [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/. --> FReq.open('GET',FURI, FALSE); // async = false but would prefer TDataset to load asynchronously using the modern (async/ await / fetch). But unfortunately, I could not find a way.

But async/await/fetch is still asynchronous.
It's just a syntax to hide this fact.
I fail to see how this is 'modern'.

your function:

function fetchAsync(url: String): TJSPromise; {$ifndef
InLazIDE}async;{$endif}
var
   response: TJSResponse;
   data: TJSPromise;
begin
   response := await(window.fetch( url ));
   Result := await(response.json());
end;

seems correct. I would expect you to use it as:

MyDataset.JSON:=await(TJSobject,fetchasync('myurl'))

Do you get an error or what is the problem you are experiencing with your function ?

Michael.

Reply by email

warleyalex
to Michael Van Canneyt

Following this piece of code:

  fConnection.Active := true; // fDataSet should be Active BUT is still
FALSE
  console.log(  fDataset.Active ); // we've got a FALSE status because we
are loading JSON in async way

The ugly workaround, I have to use the setTimeout with 50ms

  window.setTimeout(procedure()
  begin
    console.log(  fDataset.Active ); // we've got TRUE status
  end, 50);
[CODE]
var
   i, aux: Integer;
begin
  fConnection:=  TConnection.Create(nil);
  fConnection.Name := 'Connection1';
  fConnection.Active := false;
  fDataSet:=     TDataSet.Create(nil);
  fDataSet.Name := 'DataSet1';
  fDataSet.Connection := fConnection;
  fDataSource:=  TWDataSource.Create(nil);
  fDataSource.Name := 'DataSource1';
  fDataSource.DataSet := fDataSet;
  fConnection.URI := 'data.json';
  fConnection.DataNode := 'data';
  fDataSet.FieldDefs.Clear();
  fDataSet.FieldDefs.Add('Species_No', TFieldType.ftString,0);
  fDataSet.FieldDefs.Add('Category',  TFieldType.ftString,50);
  fDataSet.FieldDefs.Add('Common_Name', TFieldType.ftString,50);
  fDataSet.FieldDefs.Add('Species_Name', TFieldType.ftString,50);
  fDataSet.FieldDefs.Add('Length__cm_', TFieldType.ftInteger,0);
  fDataSet.FieldDefs.Add('Length_In', TFieldType.ftString,30);
  fDataSet.FieldDefs.Add('Notes', TFieldType.ftString,255);
  fConnection.Active := true;
  console.log(  fDataset.Active); // we've got a FALSE
  window.setTimeout(procedure()
  begin
    (* initialize / fill the first TFishRecord *)
    console.log(  fDataset.Active); // we've got TRUE status
    fishRecord := JSON2TFishRecord(
       TJSObject(fDataSet.Rows.Elements[0])
    );
    (* update de controls *)
    refreshFacts();
  end, 50);
end;
//-------------------------------------------------
(...)
{ TConnection }
function TConnection.fetchAsync(url: String): {TJSResponse} TJSPromise;
{$ifndef InLazIDE}async;{$endif}
begin
  FResponse := await(window.fetch( url ));
  Result := await(FResponse.json());
end;
procedure TConnection.SetActive(AValue: boolean);
var
  J: JSValue;
 JA: JSValue;
begin
  if ((FURI  '') and AValue) then
  begin
    DoBeforeConnect();
    FActive := AValue;
    fetchAsync(FURI)
    ._then(function(res: JSValue): JSValue
    begin
      if (FResponse.status = 200) then
      begin
        JA := TJSObject(res).Properties[FDataNode];
        DoAfterConnect();
        if (assigned(FDS)) then
        begin
          FDS.Rows := TJSArray(JA);
          FDS.Open();
        end else
        DoError(FResponse.status);
      end;
    end);

[/CODE]

Reply by email

Michael Van Canneyt
to warleyalex: Following this piece of code: fConnection.Active := true; // fDataSet should be Active…
warleyalex wrote 13 lines
Following this piece of code: fConnection.Active := true; // fDataSet should be Active BUT is still FALSE console.log( fDataset.Active ); // we've got a FALSE status because we are loading JSON in async way The ugly workaround, I have to use the setTimeout with 50ms window.setTimeout(procedure() begin console.log( fDataset.Active ); // we've got TRUE status end, 50);

I don't see how setting a connection to active can activate datasets, but we'll leave that aside for the sake of argument:

There is no need for timers. Every dataset has an AfterOpen event.

Why not use that to populate the fishrecord ?

That's how I do it in all my code. Call Load, and in the AfterOpen event handler I do whatever is needed.

That's what these event handlers are for, after all.

Michael.

Reply by email

warleyalex
to Michael Van Canneyt

Excellent!

In my crazy experimental example:

live preview: https://pas2js.github.io/master/ProjectFishfacts/index.html

I can populate the fishrecord, but in the TConnection|AfterConnect event, I could load async JSON data using Dataset.

fConnection.AfterConnect:= @AfterConn;
procedure TJFishFacts.AfterConn(Sender: TObject);
begin
  (* initialize / fill the first TFishRecord *)
  console.log(  fDataset.Active); // TRUE
end;

It is so nice, there is no need for timers or promises at all!

Reply by email