How to make one shot timer function in Delphi (like setTimeout in JavaScript)?

后端 未结 4 1872
广开言路
广开言路 2020-12-25 14:30

The setTimeout is helpful in JavaScript language. How would you create this function in delphi ?

SetTimeOut(procedure (Sender: TObject);
begin
  Self.Counter         


        
相关标签:
4条回答
  • 2020-12-25 15:10

    Assuming, the function is to be called once and not 5 times every second, maybe like that:

     Parallel.Async( 
           procedure; begin
               Sleep(200);
               Self.Counter:=Self.Counter+1; end; );
    

    There are more complex solutions like the one you accepted, taking named objects for timer actions and using SetTimer method. Like http://code.google.com/p/omnithreadlibrary/source/browse/trunk/tests/17_MsgWait/test_17_MsgWait.pas Previous versions had SetTimer with anonymous function, but they are gone now.

    However for simplistic anonymous closure approach you asked for, maybe Wait(xxX) would fit.

    0 讨论(0)
  • 2020-12-25 15:13

    I think you may leave the TTimer as it is and try to use the SetTimer function and use its callback function. You need to store the timer IDs and their (anonymous) methods in some collection. Since you didn't mentioned your Delphi version I've used a simple classes and TObjectList as a collection.

    The principle is easy, you just call the SetTimer function with the callback function specified and store the new instantiated system timer ID with the anonymous method into the collection. When that callback function is performed, find the timer which caused that callback in the collection by its ID, kill it, execute the anonymous method and delete it from the collection. Here is the sample code:

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, ExtCtrls, StdCtrls, Contnrs;
    
    type
      TOnTimerProc = reference to procedure;
      TOneShotTimer = class
        ID: UINT_PTR;
        Proc: TOnTimerProc;
      end;
      procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal);
    
    type
      TForm1 = class(TForm)
        Timer1: TTimer;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
      TimerList: TObjectList;
    
    implementation
    
    {$R *.dfm}
    
    procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
      dwTime: DWORD); stdcall;
    var
      I: Integer;
      Timer: TOneShotTimer;
    begin
      for I := 0 to TimerList.Count - 1 do
      begin
        Timer := TOneShotTimer(TimerList[I]);
        if Timer.ID = idEvent then
        begin
          KillTimer(0, idEvent);
          Timer.Proc();
          TimerList.Delete(I);
          Break;
        end;
      end;
    end;
    
    procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal);
    var
      Timer: TOneShotTimer;
    begin
      Timer := TOneShotTimer.Create;
      Timer.ID := SetTimer(0, 0, ATimeout, @TimerProc);
      Timer.Proc := AProc;
      TimerList.Add(Timer);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      SetTimeout(procedure
        begin
          ShowMessage('OnTimer');
        end,
        1000
      );
    end;
    
    initialization
      TimerList := TObjectList.Create;
      TimerList.OwnsObjects := True;
    
    finalization
      TimerList.Free;
    
    end.
    


    Simplified version (Delphi 2009 up):

    Like suggested by @David's comment, here is the same code as above, just in a separate unit with the use of generics dictionary. Usage of the SetTimeout from this unit is same as in the above code:

    unit OneShotTimer;
    
    interface
    
    uses
      Windows, Generics.Collections;
    
    type
      TOnTimerProc = reference to procedure;
      procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal);
    
    var
      TimerList: TDictionary<UINT_PTR, TOnTimerProc>;
    
    implementation
    
    procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
      dwTime: DWORD); stdcall;
    var
      Proc: TOnTimerProc;
    begin
      if TimerList.TryGetValue(idEvent, Proc) then
      try
        KillTimer(0, idEvent);
        Proc();
      finally
        TimerList.Remove(idEvent);
      end;
    end;
    
    procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal);
    begin
      TimerList.Add(SetTimer(0, 0, ATimeout, @TimerProc), AProc);
    end;
    
    initialization
      TimerList := TDictionary<UINT_PTR, TOnTimerProc>.Create;
    finalization
      TimerList.Free;
    
    end.
    
    0 讨论(0)
  • 2020-12-25 15:19

    I usually do this way

    TThread.CreateAnonymousThread(procedure begin
      Sleep(1000); // timeout
    
      // put here what you want to do
    
    end).Start;
    
    0 讨论(0)
  • 2020-12-25 15:29

    Something like

    type
    TMyProc = Procedure of Object(Sender: TObject);
    
    TMyClass = Object
        HandlerList = TStringList;
        TimerList = TStringlist;
    
      Procedure CallThisFunction(Sender :TObject); 
    
      function setTimeout(Timeout: Integer; ProcToCall : TMyProc)
    
    end;
    
    
    
    
    
    
    function setTimeout(Timeout: Integer; ProcToCall : TMyProc)
    var
      Timer : TTimer;
    begin
    
      Timer := TTimer.Create(nil);
      Timer.OnTimer := CallOnTimer;
      Timer.Interval := Timeout;
      Timer.Enabled := true;
      HandlerList.AddObject(ProcToCall);
      TimerList.AddObject(ProcToCall);
    
    end;
    
    
    function CallOnTimer(Sender : TObject)
    var TimerIndex : Integer;
        HandlerToCall : TMyProc;
        Timer : TTimer;
    begin
    
    TimerIndex :=   TimerList.IndexOfObject(Sender);
    HandlerToCall := (HandlerList.Objects[TimerIndex] as TMyProc) ;
    
    HandlerToCall(Self);
    
    HandlerList.Delete(TimerIndex);
    Timer := (TimerList.Objects(TimerIndex) as TTimer);
    Timer.Free;
    TimerList.Delete(TimerIndex);
    
    
    end;
    

    This has just been hacked together and not tested in any way but shows the concept. Basically build a list of the timers and procedures you want to call. As it is the self object is passed to the procedure when it is called but you could build a third list that contained the object to be used as a parameter in the call to setTimeout.

    The Objects are then cleaned up by freeing after the method has been called.

    Not quite the same as javascripts setTimeout but a delphi approximation.

    ps. I haven't really moved on from Delphi7 so if there is a new fangled way of doing this in Delphi XE I don't know about it.

    0 讨论(0)
提交回复
热议问题