Blog
All Blog Posts | Next Post | Previous PostSequential Code for Async Cloud Requests with Delphi Promises!
Tuesday, December 17, 2024
The Challenge
- Authenticate the user.
- Retrieve a list of available calendars.
- Fetch the events for a specific calendar.
Promising Solution
Being so used to using promises in the context of web applications, where by design, several functions work asynchronously, we missed a promises implementation for a long time for native Delphi code. Especially in the area of using REST API functions, the ability to use promises significantly simplifies writing code that needs to be built with chained asynchronous calls.
It was with much enthusiasm that we discovered the promises library written by Laurens van Run from MendriX earlier this year. This triggered us to get in touch with Laurens and see how we could both spread the message about this fantastic new Delphi promises library and also leverage this in the context of our components, especially the TMS FNC Cloud Pack that is built on top of asynchronous REST APIs.
By working together with Laurens, we also contributed to make his promises library cross-platform and offer it integrated as part of TMS FNC Core to make using it from TMS FNC Cloud Pack as seamless as possible.
We thank Laurens so much for all his efforts that went into this. Laurens did a great job for the Delphi community and his promises library is not the only one. Laurens also actively contributes to projects like DelphiCodeCoverage and Delphi-Mocks. And Laurens also revitalized the (old) Delphi SonarQube plugin and worked with Embarcadero to bring attention to it.
Together with his colleagues at Mendrix and the Delphi community, Laurens drives forward innovation in the Delphi world. Check also what jobs Mendrix has to offer for Delphi developers here: https://werkenbijmendrix.nl/
Example
//We will query two endpoints: //https://restcountries.com/v3.1/name/{CountryName} //https://restcountries.com/v3.1/alpha/{CountryCode} //Create a common request method that we can reuse function TForm1.ExecuteRestCountryRequest(APath: string; ACountryOrCode: string): IPromise<string>; begin Result := Promise.New<string>(procedure (AResolve: TProc<string>; AReject: TProc<Exception>) begin c.Request.Clear; c.Request.Host := 'https://restcountries.com/v3.1'; c.Request.Method := rmGET; c.Request.Path := APath + '/' + ACountryOrCode; c.Request.ResultType := rrtString; c.ExecuteRequest(procedure (const ARequestResult: TTMSFNCCloudBaseRequestResult) begin if ARequestResult.Success then AResolve(ARequestResult.ResultString) else AReject(Exception.Create('Request failed. No country or code found: ' + ACountryOrCode)); end); end); end;
function TForm1.GetCountryNameFromCode(ACode: string): IPromise<string>; begin Result := ExecuteRestCountryRequest('/alpha', ACode) .ThenBy(function (const AValue: string): string begin //AValue contains the JSON response, parse it to get the name: Result := GetCountryNameFromJSON(value); end); end;
Promise.New<TVoid>(procedure(AResolve: TProc<TVoid>; AReject: TProc<Exception>) begin //Do the first promisified call end) .ThenBy(function(const AResult: TVoid): IPromise<TVoid> begin Result := Promise.New<TVoid>(procedure(AResolve: TProc<TVoid>; AReject: TProc<Exception>) begin //Do the second promisified call end); end) .ThenBy(function(const AResult: TVoid): IPromise<TVoid> begin Result := Promise.New<TVoid>(procedure(AResolve: TProc<TVoid>; AReject: TProc<Exception>) begin //Do the third promisified call end); end);
procedure TForm1.GetBorderingCountires(ACountry: string); begin //Start by getting the country name ExecuteRestCountryRequest('/name', ACountry) .Op.ThenBy<TArray<string>>(function (const AValue: string): IPromise<TArray<string>> var LBorders: TArray<string>; LPromises: TArray<IPromise<string>>; I, LBorderCount: Integer; begin //Parse the returned JSON to retrieve the list of //bordering countries LBorders := GetBorderingCountriesFromJSON(AValue); LBorderCount := Length(LBorders); SetLength(LPromises, LBorderCount); //Create a promise for each country code, these are individual //requests: for I := 0 to LBorderCount - 1 do LPromises[I] := GetCountryNameFromCode(LBorders[I]); //Wait for all the promises to complete Result := Promise.All<string>(LPromises); end) .Main.ThenBy<TVoid>(function (const AValues: TArray<string>): TVoid var I: Integer; begin //Cautiously update the UI reflecting the list: if FFormNotDestroyed then begin for I := 0 to Length(AValues) - 1 do Memo1.Lines.Add(AValues[I]); end; Result := Void; end) .Main.Catch(procedure (E: Exception) begin //Show errors - if any: ShowMessage(E.Message); end); end;
Get Started and Share Your Feedback
Tunde Keller
This blog post has received 5 comments.
wiz@thetoolwiz.com
wiz@thetoolwiz.com
Bruno Fierens
Bruno Fierens
All Blog Posts | Next Post | Previous Post
wiz@thetoolwiz.com