delphi异步与javascript

news/2025/2/3 21:00:05/

delphi及C++ Builder异步处理与javascript

目录

delphi及C++ Builder异步处理与javascript

1、用于实现异步事件、异步方法、及其异步结果回调的可自定义的通用类型

2、你可引用以下基于接口化对象和异步结果的接口的抽象类,去实现异步方法或异步事件的自定义类

2.1、用于实现所有异步过程调用的基类TBaseAsyncResult

2.2、IAsyncResult异步请求结果

3、应用

3.1、我们不经意地习惯使用“同步模型”来控制代码的“顺序”执行流程

3.2、我们也习惯使用跨平台网络请求的“同步方法”来控制代码的“顺序”执行流程

3.3、如何在处理本地长耗时的网络请求中直接使用“异步模型”

4、javascript中的多线程

4.1、javascript是单线程的吗

4.2、javascript线程的执行上下文

本客户相关博文,喜欢的就点个赞,鼓励我的原创技术写作:


        继上一篇《Delphi中的匿名方法》中谈到的匿名方法,本文进一步叙述用该匿名方法,实现的异步处理。仍然是从System.Classes底层系统的RTL运行时刻库说起:

1、用于实现异步事件、异步方法、及其异步结果回调的可自定义的通用类型

TAsyncProcedureEvent = procedure (const ASyncResult: IAsyncResult) of object;TAsyncFunctionEvent = procedure (const ASyncResult: IAsyncResult; out Result: TObject) of object;TAsyncConstArrayProcedureEvent = procedure (const ASyncResult: IAsyncResult; const Params: array of const) of object;TAsyncConstArrayFunctionEvent = procedure (const ASyncResult: IAsyncResult; out Result: TObject; const Params: array of const) of object;TAsyncConstArrayProc = reference to procedure (const Params: array of const);TAsyncConstArrayFunc<TResult> = reference to function (const Params: array of const): TResult;TAsyncCallback = reference to procedure (const ASyncResult: IAsyncResult);TAsyncCallbackEvent = TAsyncProcedureEvent;

        这些可自定义具体实现方法的通用类型,均提供了IAsyncResult异步请求接口供外部传入。你可以实例化它们并传入该参数。

2、你可引用以下基于接口化对象和异步结果的接口的抽象类,去实现异步方法或异步事件的自定义类

2.1、用于实现所有异步过程调用的基类TBaseAsyncResult

        切勿将此实例作为实例引用传递。其目的是仅通过IAsyncResult接口引用此对象。不注意这一警告将导致无法预测的行为。请参阅Invoke方法的相关信息。

// System.ClassesTBaseAsyncResult = class abstract(TInterfacedObject, IAsyncResult)private typeTAsyncFlag = (Completed, Synchronous, Invoked, Cancelled, ForceSize = SizeOf(Integer) * 8 - 1); // make the size the same as Integer.TAsyncFlags = set of TAsyncFlag;privateclass constructor Create;class destructor Destroy;privateFContext: TObject;FAsyncFlags: TAsyncFlags;FInvokingThread: TThreadID;FAsyncHandle: TMultiWaitEvent;procedure SetFlagsAtomic(Value, Mask: TAsyncFlags);function GetAsyncContext: TObject;function GetAsyncWaitEvent: TMultiWaitEvent;function GetCompletedSynchronously: Boolean;function GetIsCompleted: Boolean;function GetIsCancelled: Boolean;property AsyncWaitEvent: TMultiWaitEvent read GetAsyncWaitEvent;protected/// <summary>///    This field will hold the acquired exception instance raised from the execution of the async method call.///    It will be re-raised in the context of the invoking thread when the corresponding EndXXXX method is called./// </summary>FInvokingException: TObject;/// <summary>///    Override this method to dispatch the actual asynchronous procedure call. Descendants will use whatever context///    or other state information contained in the instance to pass along to the procedure or function./// </summary>procedure AsyncDispatch; virtual; abstract;/// <summary>///    Override this method to perform any extra state or signaling required by the descendant. The descendant must///    call this inherited method in order to properly set the completion and possibly signal the FAsyncHandle if///    previously created. Failure to call this method can result in a dead lock or hang./// </summary>procedure Complete; virtual;/// <summary>///    Calls the actual target asynchronous method within the context of however it is scheduled. This could be///    in the context of the main or GUI thread, or within a background thread. This depends on the implementation///    of a specific asynchronous BeginXXXX method call./// </summary>procedure DoAsyncDispatch;/// <summary>///    Override this method to schedule the asynchronous procedure call in the manner specific to///    a given instance, component or class. By default, this will schedule the async procedure onto///    the main thread or execute the procedure synchronously if already on the main thread.///    Other classes may schedule the procedure call into a separate thread or thread pool./// </summary>procedure Schedule; virtual;/// <summary>///    This constructor must be called from a descendent protected constructor./// </summary>constructor Create(const AContext: TObject); overload;/// <summary>///    Opaque user-supplied context. This context is available via the IAsyncResult.GetAsyncContext and descendents///    if this class./// </summary>property Context: TObject read FContext;///  <summary>///    Returns true if the operation can be cancelled. When cancelling the async operation, do any additional processing.///  </summary>///  <remarks>///    By default, all Async cannot be cancelled. If descendants support cancelling asynchronous tasks,///  they must override this behaviour and do the required processing;///  </remarks>function DoCancel: Boolean; virtual;public/// <summary>///    This constructor should never be called directly. Only descendents should be constructed using the///    protected Create constructor above. Calling this constructor will raise an exception./// </summary>constructor Create; overload;destructor Destroy; override;///  <summary>///    Cancels the async operation. Returns True when the asynchronous operation can be cancelled.///  </summary>function Cancel: Boolean;/// <summary>///    This method must be called prior in order to return itself as an IAsyncResult and actually schedule/invoke the///    async call./// </summary>function Invoke: IAsyncResult;/// <summary>///    As long as the rules for only ever accessing this object through the IAsynsResult interface, this method///    should only ever be called by a given "EndInvoke" method by casting the IAsyncResult interface instance///    back to a specific descendant instance of this class. Never call this method directly outside the context///    of an "EndInvoke" style method./// </summary>procedure WaitForCompletion;/// <summary>///    This method is called from VCL.TControl (and possibly other similar) descendants in order to call the///    asynchronous procedure/function as a result of a posted Window message./// </summary>class procedure Dispatch(const AsyncResult: TBaseAsyncResult); reintroduce; static; inline;///  <summary>///    Set to True when the asynchronous call has been cancelled.///  </summary>property IsCancelled: Boolean read GetIsCancelled;end;

2.2、IAsyncResult异步请求结果

        IAsyncResult异步请求结果:是系统级别的RTL运行时刻库,分别实现了跨平台的API接口,供你使用,它来自System.Types

  /// <summary>
  ///    THTTPClient或继承于它的TRestClient客户端网络通讯库的各种“BeginXXX”方法返回的接口,以提供代码的异步执行:
  /// </summary>
  IAsyncResult = interface
    ///  <summary>
    ///    返回与此实例关联的用户指定上下文:
    ///  </summary>
    function GetAsyncContext: TObject;
    ///  <summary>
    ///    返回一个适合用于阻塞的事件,直到异步调用完成。此事件也适合在列表中使用,以允许等待所有或任何信号。请参阅TMultiWaitEvent。WaitForXXX类函数:
    ///  </summary>
    function GetAsyncWaitEvent: TMultiWaitEvent;
    ///  <summary>
    ///    如果给定的异步调用能够同步完成,则返回true。换句话说,特定调用在返回之前完成:
    ///  </summary>
    function GetCompletedSynchronously: Boolean;
    ///  <summary>
    ///    异步调用完成时返回True:
    ///  </summary>
    function GetIsCompleted: Boolean;
    ///  <summary>
    ///    取消异步调用后返回True:
    ///  </summary>
    function GetIsCancelled: Boolean;
    ///  <summary>
    ///    取消异步操作。当可以取消异步调用时,返回True:
    ///  </summary>
    function Cancel: Boolean;

    ///  <summary>
    ///    只读属性:与此实例关联的用户指定上下文:
    ///  </summary>
    property AsyncContext: TObject read GetAsyncContext;
    ///  <summary>
    ///    只读属性:事件。适用于阻塞,直到异步调用完成。此事件也适合在列表中使用,以允许等待所有或任何信号。请参阅TMultiWaitEvent。WaitForXXX类函数:
    ///  </summary>
    property AsyncWaitEvent: TMultiWaitEvent read GetAsyncWaitEvent;
    ///  <summary>
    ///    只读属性:如果给定的异步调用能够同步完成,则设置为true。换句话说,特定调用在返回之前完成:
    ///  </summary>
    property CompletedSynchronously: Boolean read GetCompletedSynchronously;
    ///  <summary>
    ///    只读属性:异步调用完成时设置为True:
    ///  </summary>
    property IsCompleted: Boolean read GetIsCompleted;
    ///  <summary>
    ///    只读属性:取消异步调用后设置为True:
    ///  </summary>
    property IsCancelled: Boolean read GetIsCancelled;
  end;

        你可以将IAsyncResult异步请求结果作为参数,传入章节1所叙述的各种异步事件或其回调类型( 1、用于实现异步事件、异步方法、及异步结果回调的通用类)。

3、应用

3.1、我们不经意地习惯使用“同步模型”来控制代码的“顺序”执行流程

3.1.1、具名线程的同步

typeThreadWechat = class(TThread)privateFifTerminated: Boolean;  //:外部控制参数_控制线程执行过程中强行中断并释放线程//F_GetTickCount_:Cardinal;//:_为了兼容IDE版本_重写继承函数GetTickCountFTerminated: Boolean;    //:继承字段_属性Terminated读/// <summary>线程当前的处理步骤:</summary>FStep:string;/// <summary>线程执行Execute中_当前微信实例在执行的_不采用匿名方法的函数:</summary>FProcObject:TThreadparamStrMethod;//procedure(var aStr:string;aCurWechat:TObject) of object;procedure _Sync_;/// <summary>线程执行Execute中_当前微信实例aCurWechat在执行什么API事务aStr:</summary>procedure doExecute_Api(var aStr:string;aCurWechat:TObject);/// <summary>线程同步Synchronize中_当前微信实例aCurWechat在同步什么API事务aStr:</summary>procedure doSynchronize_Api(var aStr:string;aCurWechat:TObject);//:以下,用上面通用Execute及其Synchronize替代_需要外部传入FApi的字符串及FcurWechat及FcurProgressBar来识别:protected/// <summary>微信类实例__是否终止了线程的执行:</summary>property Terminated: Boolean read FTerminated; //property ReturnValue:Integer read FReturnValue write FReturnValue;/// <summary>微信类实例__线程执行函数:</summary>procedure Execute; override;/// <summary>线程自旋计时_为了兼容不同的IDE版本:</summary>function _GetTickCount_: Cardinal;/// <summary>供外部询问线程处理的当前步骤:</summary>property FStep_No:string read FStep;/// <summary>供外部询问线程处理的当前方法:</summary>property FProc_WhatMethod:TThreadparamStrMethod read FProcObject;public/// <summary>传入_当前线程处理的当前微信实例调用哪个Api唯一标识_通用的不采用匿名方法:</summary>FApi:string;/// <summary>传入_当前线程处理的当前微信实例唯一标识_通用的不采用匿名方法:</summary>FcurWechat:TObject;/// <summary>传入_当前线程处理的当前微信实例的唯一同步的进度条_通用的不采用匿名方法:</summary>FcurProgressBar:TObject;//TObject; TProgressBar/// <summary>传入_线程执行Execute中_当前微信实例要执行的类方法函数_不采用匿名方法的函数:</summary>FProcObjectSync:TTickCountMethod;//procedure of Object;constructor Create(CreateSuspended: Boolean);destructor Destroy;end;

3.1.1、匿名线程的同步


procedure TFormGY.PopupMenu2Click(Sender: TObject);
beginSJGY.ShowALayoutMenu_DisposeOf(self);// ====这是名称为01的等待对话框=====SJGY.MyWaitShow('正在查询,请稍后......', self, '01', TAlphaColorRec.White, TAlphaColorRec.Red, TAlphaColorRec.Red); // 显示等待提示动画TThread.CreateAnonymousThread( // 创建一个单线程,完成ATaskprocedurebegin // 线程里的代码写这里(确保不要有对界面元素的操作)// 如保存到数据库sleep(5000); // 这里模拟等待5秒钟TThread.Synchronize(nil,procedure // 界面交互代码要用这个包起来,在主线程中执行beginSJGY.MyWaithide(self, '01'); // 隐藏等待提示动画SJGY.ToastConfirm('查询完毕!', self, 1);end);end).Start;
end;

3.1.2、匿名线程升级版的同步模型

// 欢迎使用【通用跨平台移动框架】,详询欢迎加QQ群:174483085TAnonymousThread<T1, T2> = class(TThread)privateclass var FRunningThreads: TList<TThread>;privateFThreadFunc: TFunc<T1, T2>;FOnErrorProc: TProc<Exception>;FOnFinishedProc: TProc<T1, T2>;FaT: T1;FResult: T2;FStartSuspended: boolean;privateprocedure ThreadTerminate(Sender: TObject);protectedprocedure Execute; override;publicconstructor Create(aT: T1; AThreadFunc: TFunc<T1, T2>;AOnFinishedProc: TProc<T1, T2>; AOnErrorProc: TProc<Exception>;ACreateSuspended: boolean = False; AFreeOnTerminate: boolean = True);class constructor Create;class destructor Destroy;end; //:来超备注2021-10-12:TAnonymousThread:SJGY.ShowDownLoadDialog下载对话框中应用:很有用// =========匿名线程增强结束>>>>>>>>>>>>>>================

3.2、我们也习惯使用跨平台网络请求的“同步方法”来控制代码的“顺序”执行流程

    try
      MyHTTPResponse := MyHTTPClient.Get(LowerCase(Wx_ApiProxy + Getkflist + '?') + queryString, nil, nil );
      Result_SyncCode:= MyHTTPResponse.StatusCode; // = 200 本系统的返回码__非微信的
      try
        ResultJsonStr_MyHTTPResponse := (MyHTTPResponse.ContentAsString(TEncoding.UTF8));
        // 客服头像的URL链接,服务端不应当URL解码给客户端否则不安全,客户端解析的时候注意应当URL解码“\/”等特殊符号__TNetEncoding.URL.Decode
      finally
        if Assigned(JO1_MyHTTPResponse) then JO1_MyHTTPResponse.DisposeOf;
      end;
    except // Wx异常有异常时专用的响应 :
      try
        ResultJsonStr_MyHTTPResponse := (MyHTTPResponse.ContentAsString(TEncoding.UTF8));
        JO1_MyHTTPResponse := TJSONObject.ParseJSONValue( ResultJsonStr_MyHTTPResponse ) as TJSONObject;
        if JO1_MyHTTPResponse <> nil then
        begin
          Result_SyncCode:= JO1_MyHTTPResponse.GetValue<integer>('errcode');
          Resolution_WxAPIErrcode( Result_SyncCode); // 错误处理
        end;
      finally
        ResultJsonStr_MyHTTPResponse := '';
        if Assigned(JO1_MyHTTPResponse) then JO1_MyHTTPResponse.DisposeOf;
      end;
 

3.2.1、你会习惯性地使用:

function THTTPClient.Get(const AURL: string; const AResponseContent: TStream; const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Post(const AURL: string; const ASourceFile: string;
  const AResponseContent: TStream; const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Put(const AURL, ASourceFile: string;
  const AResponseContent: TStream; const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Patch(const AURL: string; const ASource, AResponseContent: TStream;
  const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Delete(const AURL: string; const AResponseContent: TStream; const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Options(const AURL: string; const AResponseContent: TStream;
  const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.GetRange(const AURL: string; AStart, AnEnd: Int64; const AResponseContent: TStream;
  const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Trace(const AURL: string; const AResponseContent: TStream; const AHeaders: TNetHeaders): IHTTPResponse;

function THTTPClient.Head(const AURL: string; const AHeaders: TNetHeaders): IHTTPResponse;

       等等,它们,从本质上来讲,都是“阻塞式”的“网络请求”,即在网络客户端默认的或你自定义的请求和响应超时时间内,阻塞式地完成请求并返回响应的接口,直到返回响应或返回超时为止。内部,它是这样工作的:

// System.Net.HttpClient.pas
function THTTPClient.Execute(const ARequest: IHTTPRequest; const AContentStream: TStream; const AHeaders: TNetHeaders): IHTTPResponse;
var
  LHeader: TNetHeader;
begin
  if ARequest.IsCancelled then
    (ARequest as THTTPRequest).DoResetCancel;

  if AHeaders <> nil then  //: 遍历请求头,设置Header头的键值对数值 :
    for LHeader in AHeaders do
      ARequest.SetHeaderValue(LHeader.Name, LHeader.Value);

// System.Net.URLClient.pas
  // : 获取响应的实例并执行它,但它内部直接忽略了三个异步参数为nil, nil, nil, :
  Result := DoGetResponseInstance(Self, nil, nil, nil, ARequest, AContentStream) as IHTTPResponse;
  ExecuteHTTP(ARequest, AContentStream, Result);
end;

// DoGetResponseInstance的原型:

function TURLClient.DoGetResponseInstance(const AContext: TObject;        // 执行上下文的实例const AProc: TProc;                            // 传入的异步执行的匿名方法const AsyncCallback: TAsyncCallback;           // 需要回调的异步方法const AsyncCallbackEvent: TAsyncCallbackEvent; // 需要回调的异步事件 const ARequest: IURLRequest;    // URI请求的接口类型的实例const AContentStream: TStream   // 响应的内容流
): IAsyncResult; // 返回异步结果的操作系统API实现的接口类型的实例
beginraise ENetURIClientException.CreateRes(@SNetSchemeFunctionNotImplemented);// 当网络发生故障时,返回错误提示字符串并释放相关的内部资源引用
end;

可见,同步模型下,直接忽略了三个异步模型相关的类型参数:

  const AProc: TProc;                                                 // 传入的异步执行的匿名方法
  const AsyncCallback: TAsyncCallback;                   // 需要回调的异步方法
  const AsyncCallbackEvent: TAsyncCallbackEvent; // 需要回调的异步事件 

3.2.2、即:默认的各种请求,我们是没有刻意地去引用“响应接口”的“异步结果接口属性”的:

  IURLResponse = interface(IInterface)['{5D687C75-5C36-4302-B0AB-989DDB7558FE}']//......(省略)// 默认的各种请求,我们是没有刻意地去引用异步结果接口属性的:/// <summary>AsyncResult异步结果接口属性的Getter:</summary>function GetAsyncResult: IAsyncResult;/// <summary>对异步结果接口IAsyncResult的引用,用于控制相应请求的异步执行:</summary>property AsyncResult: IAsyncResult read GetAsyncResult;end;

       一旦,你引用了“异步结果接口属性”,或者直接用3.3、中的“异步模型”进行网络请求,那么,请求及其响应,将会以“异步的方式”被执行

3.2.2、请求引用“异步结果接口属性”执行异步响应

var aAsyncResult : IAsyncResult;
var aAsyncWaitResult : TWaitResult;
var aMultiWaitEvent : TMultiWaitEvent;
var aWaitStr : string;if access_token='' thenbeginResult := GetJsonString('access_token不能为空');exit;end;MyHTTPClient := THTTPClient.Create;MyHTTPClient.ConnectionTimeout := 5000; // 5秒MyHTTPClient.ResponseTimeout := 10000;  // 10秒MyHTTPClient.ContentType := 'application/json; charset=UTF-8';MyHTTPClient.UserAgent := 'Embarcadero URI Client/1.0';MyHTTPClient.AutomaticDecompression := [THTTPCompressionMethod.Deflate,THTTPCompressionMethod.GZip,THTTPCompressionMethod.Brotli,THTTPCompressionMethod.Any ];queryString:='access_token='+access_token; tryaAsyncResult := MyHTTPClient.Get(LowerCase(Wx_ApiProxy + Getkflist + '?') + queryString, nil, nil ).AsyncResult;aMultiWaitEvent := aAsyncResult.AsyncWaitEvent.create;aAsyncWaitResult := aMultiWaitEvent.waitfor(0);while ( aAsyncWaitResult >= 1 )  do // 内部线程的原子信号量等待__不阻塞beginbreak;end; //TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError, wrIOCompletion);//TWaitResult = (0, 1, 2, 3, 4);aMultiWaitEvent.SetEvent; // 显式的发出信号if aAsyncResult.IsCompleted then // :如果网络请求被异步地执行完毕,无论对与错,那么:beginif aAsyncWaitResult = TWaitResult.wrTimeout then aWaitStr :='网络超时...';if aAsyncWaitResult = TWaitResult.wrError then aWaitStr :='网络错误...';if aAsyncWaitResult = TWaitResult.wrAbandoned then aWaitStr :='请求被放弃了...';if aAsyncWaitResult = TWaitResult.wrIOCompletion then aWaitStr :='I/O完成了...';if aAsyncWaitResult = TWaitResult.wrSignaledResult thenbeginaWaitStr :='正确返回...';// 拿到响应结果后,你想做的事情......end;end;finallyMyHTTPClient.DisposeOf;end;

3.2.3、但是,的确,你很少会去关注和直接使用它们分别拥有的3种异步“重载”方法:

3.3、如何在处理本地长耗时的网络请求中直接使用“异步模型”

3.3.1、网络请求中的“异步模型”

        如上所述(3.2),如果我们关注每一种请求类型的3种异步“重载”方法,比如Get请求:

    /// <summary>向url发送异步“GET”命令</summary>
    function BeginGet(const AURL: string; const AResponseContent: TStream = nil;
      const AHeaders: TNetHeaders = nil): IAsyncResult; overload;
    function BeginGet(const AsyncCallback: TAsyncCallback; const AURL: string; const AResponseContent: TStream = nil;
      const AHeaders: TNetHeaders = nil): IAsyncResult; overload;
    function BeginGet(const AsyncCallbackEvent: TAsyncCallbackEvent; const AURL: string;
      const AResponseContent: TStream = nil; const AHeaders: TNetHeaders = nil): IAsyncResult; overload;

       我们便能够拿到一个“异步结果”的接口IAsyncResult。并且后两种异步“重载”方法,还可以执行方法TAsyncCallback或事件TAsyncCallbackEvent的“异步回调”(其类型声明详见本文章节1:“1、用于实现异步事件、异步方法、及其异步结果回调的可自定义的通用类型”)。

    // Result_AsyncCode:= -9999;tryMyHTTPClient := THTTPClient.Create;MyHTTPClient.ConnectionTimeout := 5000; // 5秒MyHTTPClient.ResponseTimeout := 10000;  // 10秒MyHTTPClient.ContentType := 'application/json; charset=UTF-8';MyHTTPClient.UserAgent := 'Embarcadero URI Client/1.0';MyHTTPClient.AutomaticDecompression := [THTTPCompressionMethod.Deflate,THTTPCompressionMethod.GZip,THTTPCompressionMethod.Brotli,THTTPCompressionMethod.Any];tryResult := ResultJsonStr_MyHTTPResponse; // :如果是函数的话,在异步模型中,function的返回值已失去意义void(0),相当于一个procedureContentStreamResult:=TStringStream.Create('',TEncoding.UTF8);AsyncCallback := TAsyncCallback(procedure(AsyncResult:IAsyncResult)begin{ // 如果你需要内部拦截等待:aMultiWaitEvent := AsyncResult.AsyncWaitEvent.create;aAsyncWaitResult := aMultiWaitEvent.waitfor(0);while ( aAsyncWaitResult >= TWaitResult.wrSignaled ) do  // 内部线程的原子信号量等待__不阻塞beginif aAsyncWaitResult <> TWaitResult.wrIOCompletion then break;ResultJsonStr_MyHTTPResponse := ContentStreamResult.DataString;end; //TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError, wrIOCompletion);//TWaitResult = (0, 1, 2, 3, 4);FreeAndNil(aMultiWaitEvent); // }// 异步模型下,传入的内容流A,异步执行结果的响应对象的流B, 当响应结束后_无论成功或失败,A流的字节大小===B流的字节大小 :if ( (AsyncResult as THttpResponse).GetContentStream.Size = ContentStreamResult.Size  ) then //if ( (AsyncResult as THttpResponse).GetContentStream.Equals(ContentStreamResult)  ) then// if ( (AsyncResult as THttpResponse).GetStatusCode >=0  ) then  // 相应类的实现THTTPResponse = class(TURLResponse, IHTTPResponse)begin //TThread.Current.Synchronize(nil,procedure begin ShowMessage((AsyncResult as THttpResponse).GetStatusCode.ToString) end); // GetStatusCode = 200// + sLineBreak + '异步结果IAsyncResult的执行上下文的类名...'+TObject(AsyncResult.AsyncContext).ClassName; // 异步结果IAsyncResult的执行上下文的类名...为该跨平台的THTTPClient库_比如MSWindows下为---TWinHTTPClientResultJsonStr_MyHTTPResponse:=ContentStreamResult.DataString;TThread.Current.Synchronize(nil,procedure begin EnableLogMe:=true; LogMe(Application.MainForm,'ContentStreamResult.DataString:'+ResultJsonStr_MyHTTPResponse + sLineBreak + '异步结果IAsyncResult的执行上下文的类名...'+TObject(AsyncResult.AsyncContext).ClassName ); EnableLogMe:=false; end); // 内部日志if Assigned(proc) then TThread.Current.Synchronize(nil,procedure begin proc(ResultJsonStr_MyHTTPResponse); end) else TThread.Current.Synchronize(nil,procedure begin ShowMessage('你不传入方法,我也没法为你做事并回传给你看'); end);// :如果外部传入了方法,我就在【网络-异步模型】的内部按照这个方法帮你干!if MyHTTPClient<>nil then MyHTTPClient.DisposeOf;FreeAndNil(ContentStreamResult);FreeAndNil(aStrlist);end;end);  // 发出异步请求 :MyHTTPClient.BeginGet( AsyncCallback, LRequestURL, ContentStreamResult, aHeaders );// :这是异步调用;同步的话: MyHTTPClient.Get( LRequestURL, nil, nil );except// ...(略)end;finallyend;

        原理:它内部的本质,仍旧是,后台线程的安全锁,底层代码如下:

// System.Net.HttpClientfunction THTTPClient.InternalExecuteAsync(const AsyncCallback: TAsyncCallback; const AsyncCallbackEvent: TAsyncCallbackEvent;const ARequest: IHTTPRequest; const AContentStream: TStream;const AHeaders: TNetHeaders; AOwnsSourceStream: Boolean): IAsyncResult;
varLHeader: TNetHeader;LContentStream: TStream;LAsync: IAsyncResult;LRequest: THTTPRequest;
{$IFDEF AUTOREFCOUNT}LSourceStream: TStream;
{$ENDIF}
beginif ARequest.IsCancelled then(ARequest as THTTPRequest).DoResetCancel;if AHeaders <> nil thenfor LHeader in AHeaders doARequest.SetHeaderValue(LHeader.Name, LHeader.Value);LRequest := ARequest as THTTPRequest;if AOwnsSourceStream thenLRequest.FOwnedStream := LRequest.FSourceStream
{$IFDEF AUTOREFCOUNT}elseLSourceStream := LRequest.FSourceStream
{$ENDIF};LContentStream := AContentStream;LAsync := DoGetResponseInstance(Self,procedurebegintryExecuteHTTP(ARequest, LContentStream, (LAsync as THttpResponse));// : !!!!关键!!!!!!!!LAsync as THttpResponse//   : 内部处理了线程中的网络请求的事件循环,响应流、Cookies管理、状态码、中断、与异常//   :    特别的,状态码,处理了200、及401和407的失败回调(如果前置的身份验证authentication失败,则回退到正常路径)finally
{$IFDEF AUTOREFCOUNT}LSourceStream := nil;
{$ENDIF}end;end, AsyncCallback, AsyncCallbackEvent, ARequest, AContentStream);Result := LAsync;// Invoke Async Execution.!!!!关键---线程中的请求结果及调度:!!!!!!!!(LAsync as THttpResponse).Invoke;
end;

        其中(详见:2.1、用于实现所有异步过程调用的基类TBaseAsyncResult): 

// System.Classesfunction TBaseAsyncResult.Invoke: IAsyncResult;
beginSetFlagsAtomic([TAsyncFlag.Invoked], [TAsyncFlag.Invoked]);FInvokingThread := TThread.CurrentThread.ThreadID;// :当前执行请求的线程引用计数,返回异步请求结果,并进行事件、方法等调度 :_AddRef;Result := Self;Schedule;
end;

3.3.2、处理本地长耗时的网络任务的两种“通用方法”

3.3.2.1、直接使用“异步请求”的网络模型、传入回调并执行

procedure TForm1.ThreadExec_WechatMethod(aProcName:string='');
var proc: TProc<string>;//LWxApi:TWxApi; aProcObjectSync:TTickCountMethod;//......(,仅为示例,详细,略)......if ( trim(aProcName) = 'Customservice_Getkflist' ) thenbegin//:___定义并启动___◆对话服务_获取所有的客服人员的列表___://callApiOfTWxApi_InThread( LWxApi, trim(aProcName) );//   :===============这是线程中的网络同步模型; 现在换一种方法__调用网络异步模型 :  ========================================================proc := procedure(aStr:string) begin {写入方法:}Memo1.Lines.Add(aStr);  {传入事件:}Button_OKClick(Text_Debug); end;HTTPClientHandler_Get_Async ( trim(aProcName), nil, TProc<string>(proc) ); //: 传入空的回调方法则不会被执行 :TProc<string>(nil)FreeAndNil(LWxApi);end;
end;

3.3.2.2、使用线程,在后台线程的同步函数中处理传入的“回调”

type//TTickCountProcedure = TProcedure;TTickCountMethod = procedure of Object;//  :某个类的实例方法TThreadparamStrMethod = procedure(var aStr:string;aCurBaidupan:TObject) of Object;//  :线程类中带字符串及对象参数的类实例方法

         定义需要在后台线程中执行的“网络请求”的通用方法

procedure TForm1.ThreadExec_WechatMethod(aProcName:string='');
var // proc: TProc<string>; // 改用后台线程同步回调 :LWxApi:TWxApi; aProcObjectSync:TTickCountMethod;/// <summary>类的实例中是否发布了该方法:</summary>/// <remarks>下来请进一步研究System.Classes.TClassFinder</remarks>function ifMethodIsDefined(aClassInstance:TObject;aMethodName:string):Boolean;var aMethodObject:TTickCountMethod;beginif (aClassInstance=nil) or (trim(aMethodName)='') thenbeginShowMessage('在执行判断类实例是否定义了某方法的内部函数时,传参错误...'); Result:=false;end elsebegin@aMethodObject:=aClassInstance.MethodAddress(aMethodName);if @aMethodObject = nil thenbeginFreeAndNil(aClassInstance); ShowMessage('该API尚未定义无法调用...'); Result:=false;end else Result:=true;end;end;/// <summary>返回已在类实例中发布的某方法:</summary>/// <remarks>下来请进一步研究System.Classes.TClassFinder</remarks>function getMethodDefined(aClassInstance:TObject;aMethodName:string):TTickCountMethod;var poinerRecordOfMethod:TMethod;// 方法("包含的指针的code及data")的记录类型beginResult := nil;if (aClassInstance<>nil) and (trim(aMethodName)<>'') thenbeginpoinerRecordOfMethod.Code := Pointer(aClassInstance.MethodAddress(aMethodName));poinerRecordOfMethod.Data := Pointer(aClassInstance);end;if Assigned( poinerRecordOfMethod.Code ) then // 如果定义了该方法,则返回该方法Result := TTickCountMethod( poinerRecordOfMethod );// TProcedure<可选参数>针对function ; TTickCountMethod针对procedure (可选参数) of objectend;/// <summary>在线程池的某线程实例中执行对象池中微信类TWxApi的某实例中发布的某个API方法:</summary>/// <param name="aClassInstance">微信类TWxApi的某个实例.</param>/// <param name="aMethodName">微信类实例中发布的某个API方法.</param>/// <remarks></remarks>procedure callApiOfTWxApi_InThread(aClassInstance:TObject;aMethodName:string);beginif ifMethodIsDefined( aClassInstance,trim(aMethodName) ) thenbeginaProcObjectSync := getMethodDefined( aClassInstance,trim(aProcName) );FapiOfMyWechat.Add( aClassInstance );ThreadExec_WechatMethod( aClassInstance, trim(aProcName), aProcObjectSync );end;end;
begin//___FapiOfMyWechat微信对象池___FaThreads_Wechats处理业务的后台线程池___if FaThreads_Wechats = nil then FaThreads_Wechats := TList.Create;//___产生微信类的实例___:LWxApi := TWxApi.Create;LWxApi.FStatus := stWxT_AddedtoList;//:微信对象实例的___运行状态类型仓库管理___:Ord(LWxApi.FStatus)=1if ifMethodIsDefined( LWxApi,trim(aProcName) ) = false then exit;if ( trim(aProcName) = 'Customservice_Getkflist' ) thenbegin//:___定义并启动___◆对话服务_获取所有的客服人员的列表___:callApiOfTWxApi_InThread( LWxApi, trim(aProcName) );//   :===============这是线程中的网络同步模型;end;
end;

        在后台线程中执行该方法及其回调,并加入线程池统一管理该线程: 

procedure TForm1.ThreadExec_WechatMethod(aWxApi:TObject;aProcName:string;aProcObjectSync:TTickCountMethod);
var LThreadWechat:ThreadWechat;
begin//定义线程:LThreadWechat := ThreadWechat.Create(true);LThreadWechat.FreeOnTerminate:=true;LThreadWechat.FcurWechat := aWxApi; // :传入的微信类实例LThreadWechat.FApi := aProcName;    // :传入调用的●API的方法函数名●LThreadWechat.FProcObjectSync := aProcObjectSync;//LWxApi.Get_access_token;//线程加入列表___线程池___统一管理便于将来释放等处理:FaThreads_Wechats.Add( LThreadWechat );//唤醒并启动运行(列表___线程池中的)该线程:tryLThreadWechat.Resume; //ThreadWechat( FaThreads_Wechats.Items[ Lcount_WxApi ] )CheckSynchronize;finallyend;
end;

3.3.3、异步模型与同步模型的比较

  • 同步模型:
  •         灵活性:你可以用自定义线程和线程池,管理你的应用中来自网络的和非网络的任务,“不阻塞”的执行、或让其“阻塞”的执行。
  •         功能性:取决于你的应用和代码的“设计模式”的架构与规划。
  •         可扩展性:较好,完全取决于“功能性”。
  •         风险性:如果不精通线程的设计与调度,可能会发生不可预期的“用户态”的系统错误。特别是 I / O冲突,包括来自网络的i/o及来自磁盘读写的i/o(磁盘i/o速度明显低于网络、内存及cpu的处理速度),所以如果在服务端访问第三方平台的API,在并发环境中你可能会期望”使用线程“来避免阻塞,此时,就特别要处理好CPU和I/O的协同和步调,要力争使其步调协调;另外,关于”并行“一般不建议未经特别的代码处理直接用于服务端,它会使cpu多核心同时工作高负荷运行,一般只在特别时段、处理特别事务中使用;否则在处理来自客户端请求的高并发环境中,会使得cpu感觉”疲惫“而响应客户的UE体验。
  • 异步模型:
  •         灵活性:不可自主,一般是做底层封装,如果您有能力也可自行封装;通常是由你使用的编程工具或IDE对接各操作系统底层的“通信模型”来具体实现的。
  •         功能性:受制于编程工具或IDE对各操作系统的“线程模型”的类型封装。
  •         可扩展性:“系统性”较强,取决于编程工具或IDE厂商的规划与进度。比如,浏览器的内核及其开发工具,对H5规范中js的Promise及ES6中async异步模型的语言特征的统一支持和跨平台功能实现。
  •         风险性:较小,编程工具或IDE厂商会系统性地QA及QC处理。I/O方面,它内部已经以统一的模式进行了处理(当然不够灵活)。

4、javascript中的多线程

4.1、javascript是单线程的吗

        “javascript是单线程的!”,网上流行这种说法,说的人多了,貌似也就“约定俗成”了。

        其实这种说法是不够严谨的,确切的说,“单线程”和“多线程”,本身从概念上讲,是程序“运行时”的属性,某段程序,如果它不被执行或调用,就谈不上说“单线程”还是“多线程”;因而“单线程”还是“多线程”,是程序代码在“运行时”的上下文环境。在程序执行的“上下文环境”中,是单个线程在工作,还是多个线程在同时工作或协同,最初,因为电脑的CPU都是单核心的,我们知道单个CPU在单位时间内就只能作一个worker处理一件“事务”,当时,就只有“单线程”这一种模型;后来,多核心的CPU出现了,多个CPU能够能在同一时刻分别处理不同的事情。

        “javascript是单线程”的说法,跟javascript的成长历史有关,最初,它就是为网页做些个“特效”而出现的,当时的浏览器也只是做一些个技术类“文章”写作与共享,浏览器也期望能丰富“互联网”的上的“应用”的范围和实效。那个年代,在服务端,根本没有javascript的身影,只在“浏览器”中应用一些简单功能。

4.2、javascript线程的执行上下文

4.2.1、服务端

       在多核心CPU硬件环境下,nodejs能够处理和运行”多线程“。

4.2.2、客户端

       在浏览器或嵌入式浏览器(比如App中的webview)环境下,在网页的主线程(即浏览器进程内置js的解释引擎中,负责处理该页面的”渲染线程“ [ 渲染:不熟悉浏览器及其js工作原理的朋友,可以理解为UI界面在被像素化地”绘制“出来之前的内容的生成和呈现过程 ])中,浏览器只允许javascript代码,以”单个线程“的方式进行作业,这,是由js的宿主,浏览器来决定的。

       js代码如何在多线程环境下工作?

       多核环境下:(1)、js代码,可以同时运行在”不同页面对应的后台导航器中Navigator,即我们通常所说的专用worker“(即浏览器的非”渲染“子进程);(2)、不同的js模块,它们还可以工作于浏览器的”后台服务worker“中。(3)、1个个单个的js模块,它也可以分别同时运行在浏览器的不同”标签页“中,通过”多标签页“共享资源。头两种,均不可直接操作负责渲染的页面的”DOM“;而且,不同浏览器的兼容性也各异;当然,绝大部分的功能,绝大部分浏览器的最新版本都基本支持了。

       的确,还是有不少限制的,毕竟js还是”年轻的“。

       另外就是,js是”弱类型“的,代码多了,特别是”堆“大了,很容易引发因”类型“问题导致的”不宜被发现“的异常。typescript的诞生和逐渐成熟,将会彻底改变和补充这一现状,使得js能支持”强类型“。顺便说下ts,他的发明人和主导者,Anders Hejlsberg安德斯·海尔斯伯格,即咱们一直在使用的Turbo Pascal语言编译器、delphi及c++ 的Builder的IDE的发明人,他还是C#之父,.NET的创立者。

本客户相关博文,喜欢的就点个赞,鼓励我的原创技术写作:

1、多线程

2、RTTI运行时的类型信息

3、在delphi和C++ Builder中使用JavaScript

4、JavaScript


http://www.ppmy.cn/news/2819.html

相关文章

web前端期末大作业:基于HTML+CSS+JavaScript制作鲜花礼品在线购物网站设计(19页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

如何下载不同格式的卫星地图

如何下载不同格式的卫星地图 发布时间&#xff1a;2018-01-17 版权&#xff1a; 务名称&#xff1a;下载任务的名称&#xff0c;可重命名 经纬度范围当前下载区域所在的经纬度范围&#xff0c;不可更改 保存路径&#xff1a;下载你选择区域的文件保存的地方&#xff0c;可更…

html多个好看的背景动态效果(附源码)

文章目录1.设计来源1.1 图片轮动背景1.2 星空流星背景1.3 动态美女背景1.4 动态屋雨背景1.5 动态街道背景1.6 动态夜幕背景2.效果和源码2.1 动态效果2.2 透明度配置2.2 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/arti…

22 条 API 设计的最佳实践

在这个微服务的世界里&#xff0c;后端API的一致性设计是必不可少的。 今天&#xff0c;我们将讨论一些可遵循的最佳实践。我们将保持简短和甜蜜——所以系好安全带&#xff0c;出发咯&#xff01; 首先介绍一些术语 任何API设计都遵循一种叫做“面向资源设计”的原则&#…

聊聊与前端工程师天然互补的 Serverless

作为前端工程师&#xff0c;我们的使命是为用户提供良好的前端用户体验。随着云原生时代的到来&#xff0c;显而易见的&#xff0c;我们能做的更多了。Serverless 产品的特点是免运维、按量付费和自适应弹性&#xff0c;所以我们可以利用云上的各种 Serverless 能力&#xff0c…

目标检测算法——图像分割数据集汇总 2(附下载链接)

>>>深度学习Tricks&#xff0c;第一时间送达<<< &#x1f384;&#x1f384;近期&#xff0c;小海带在空闲之余&#xff0c;收集整理了一批图像分割数据集供大家参考。 整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff01;&#xff01;&am…

JDBC 数据库连接池之Driud

1 数据库连接池简介 数据库连接池是个容器&#xff0c;负责分配、管理数据库连接(Connection) 它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个&#xff1b; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据…