Skip to content

TPromiseResult<T> is a new type for web applications made with Delphi and TMS Web Core

License

Notifications You must be signed in to change notification settings

svanas/PromiseResult

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

PromiseResult

TPromiseResult<T> is a new type for web applications made with Delphi and TMS Web Core.

The problem

You can call await() on any function that returns a TJSPromise and basically turn an asynchronous function call into a blocking function call.

When the promise gets resolved, await() will return the expected type and life is good. But when a promise gets rejected, pas2js will translate this into an exception.

There is nothing wrong with exceptions, but you do need to catch them in a try..except block or your web application will panic.

The bigger issue is that with web applications, everything is a JSValue and this includes exception objects. Your exception object could be derived from Exception or from TJSError or anything else. We don't know. This makes error handling prone to... errors.

The solution

When you await for TPromise<T>.Execute() then this function will ALWAYS return a TPromiseResult<T>

You don't need a try..except block and neither do you need a lot of boiler code to get the error message, potentially not getting the error message at all.

If the promise got resolved, TPromiseResult<T>.IsResolved is True and you can get the return value via TPromiseResult<T>.Value. But if the promise got rejected, TPromiseResult<T>.IsRejected is True and you can get the error message via TPromiseResult<T>.Error:

procedure TForm1.WebButton1Click(Sender: TObject);
var
  PR: TPromiseResult<string>;
begin
  PR := await(TPromiseResult<string>, TPromise<string>.Execute(@MyAsyncFunc));
  if PR.IsResolved then
    console.log('resolved: ' + PR.Value)
  else
    console.error('rejected: ' + PR.Error.Message);
end;

Then there are other methods such as TPromiseResult<T>.ifResolved and TPromiseResult<T>.ifRejected that allow for a functional programming style if you want. Here is an example:

procedure TForm1.WebButton1Click(Sender: TObject);
begin
  await(TPromiseResult<string>, TPromise<string>.Execute(@MyAsyncFunc))
    .ifResolved(procedure(value: string)
    begin
      console.log('resolved: ' + value)
    end)
    .&else(procedure(error: TPromiseError)
    begin
       console.error('rejected: ' + error.Message);
    end);
end;

Implementing a promise

If you ever find yourself in a promise executor and you need to reject the promise, you can create an Exception but if you are using PromiseResult in your project, it is recommended to do this:

function MyAsyncFunc: TJSPromise;
begin
  Result := TJSPromise.New(
    procedure(resolve, reject: TJSPromiseResolver)
    begin
      reject(TPromiseError.Create('my error message'));
    end
  );
end;

Custom errors

Creating a class that derives from TPromiseError allows for you to introduce your own custom errors. Here is an example:

type
  TMyCustomError = class(TPromiseError)
  strict private
    FStatusCode: Integer;
  public
    constructor Create(const aMsg: string; const aStatusCode: Integer);
    function Message: string; override;
  end;

constructor TMyCustomError.Create(const aMsg: string; const aStatusCode: Integer);
begin
  inherited Create(aMsg);
  FStatusCode := aStatusCode;
end;

function TMyCustomError.Message: string;
begin
  Result := Format('%d: %s', [FStatusCode, FMessage]);
end;

Now that you have defined your own custom error, here is how to reject a promise:

function MyAsyncFunc: TJSPromise;
begin
  Result := TJSPromise.New(
    procedure(resolve, reject: TJSPromiseResolver)
    begin
      reject(TMyCustomError.Create('my custom error message', 404));
    end
  );
end;

About

TPromiseResult<T> is a new type for web applications made with Delphi and TMS Web Core

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages