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{ ---EndAsyncHTTP( 你的THttpClient异步请求 )拿到响应对象接口 : 异步若需等待并为调用者返回结果、或宝异常: 只能这样处理// 如果你仅仅需要内部拦截等待: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);  // 发出异步请求 :MyHTTPResponse:=MyHTTPClient.EndAsyncHTTP( // : 异步若需等待并为调用者返回结果、或宝异常: 只能这样处理MyHTTPClient.BeginGet( AsyncCallback, LRequestURL, ContentStreamResult, aHeaders ));); // :这是异步调用;同步的话: MyHTTPClient.Get( LRequestURL, nil, nil );exceptHttpResult := '网络异常或服务器故障,请检查...'; // 捕获网络异常、服务器未启动或不工作等故障end;finallyif HttpResult = '网络异常或服务器故障,请检查...' then Result := HttpResultelse Result := MyHTTPResponse.ContentAsString(TEncoding.UTF8);end;

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

// 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

delphi异步与javascript相关推荐

  1. html js异步绑定,JavaScript异步机制介绍

    异步就是代码执行的顺序,并不是按照从上到下的顺序一次性执行,而是在不同的时间段执行,一部分代码在"未来执行".本文就来为大家介绍一下JavaScript中的异步机制. 单线程异步执 ...

  2. php js 异步上传图片,javascript实现异步图片上传方法实例

    如何通过javascript写出异步图片上传?本文我们就和大家分享一些实例代码javascript实现异步图片上传.我们首先看下HTML代码实现的form提交部分.其中大家在测试的时候需要把test的 ...

  3. 在 Delphi 中调用 JavaScript(二)

    本例效果图: 代码文件: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Co ...

  4. Ajax异步与JavaScript的一些初浅认识

    向服务器请求数据的技术 有以下五种常用技术用于向服务器请求数据 XMLHttpRequest(XHR) Dynamic script tag insertion(动态脚本标签插入) iframes C ...

  5. delphi 异步 调用 带参数_如何在 Spring 异步调用中传递上下文

    什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行.异步调用指,在程序在执行时,无 ...

  6. delphi 异步 调用 带参数_Dubbo 关于同步/异步调用的几种方式

    我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制:基于这种机制,Dubbo 实现了以下几种调用方式: 同步调用 异步调用 参数回调 事件通知 同步调用 同 ...

  7. Delphi中的匿名方法

    Delphi及C++ Builder中底层的匿名方法及泛型 目录 Delphi及C++ Builder中底层的匿名方法及泛型 1.System.SysUtils 2.System.Classes 3. ...

  8. 一般处理程序可以类似路由的_网络核心动作路由处理程序和方法

    一般处理程序可以类似路由的 介绍(Introduction) I am going to discuss four interrelated terms and the code that they ...

  9. JQuery日记6.5 Javascript异步模式(一)

    理解力JQuery前实现异步队列,有必要理解javascript异步模式. Javascript异步其实并不严重格异步感,js使某些片段异步方式在将来运行,流不必等待继续向下进行. 在多线程的语言中最 ...

最新文章

  1. 数据库的三大范式和事物
  2. 6 Springboot 整合Redis
  3. Java里的按值传递与引用传递
  4. multipath管理存储多路径
  5. 【在线报表设计】提升报表外观的15个技巧
  6. Quartz学习总结(2)——定时任务框架Quartz详解
  7. 【Java笔记】四种权限修饰符总结
  8. 【学时总结】 ◆学时·IV◆ 数位DP
  9. 戴尔服务器安装win2008找不到硬盘
  10. 谷歌神经网络机器翻译NMT:人人可利用TensorFlow快速建立翻译模型
  11. 基于Tensorflow深度学习的ECG身份识别方法(二)
  12. demo h5 touch 移动_移动端Touch事件与H5-Canvas像素点检测实现刮刮乐
  13. 【IT女神勋章挑战赛名单公布】:看看谁获奖啦!
  14. EF的Code First开发系列之动手写第一个Code First应用
  15. 高等数学求导积分公式
  16. 1178_hardware_basic_什么是高阻态
  17. JAVA 设计模式(三)—— 设计模式之工厂模式
  18. 5 打印选课学生名单
  19. 非技术性的面试中的技巧性回答集锦(建议收藏)
  20. Excel 2010 VBA 入门 144 制作通用工具

热门文章

  1. 【简单详细】为Unity游戏制作开场动画video,新手必看
  2. 关于WPF的资源引用问题
  3. Cannot find SourceMap 'XXX.js.map'问题解决
  4. 深度linux系统初始化,deepin官方论坛-深度科技官网旗下网站
  5. 铁甲雄兵哪个服务器人最多,超级玩家第三期——铁甲雄兵全区最高等级玩家
  6. 高德地图PC版国内首发室内地图
  7. 神经网络架构搜索——可微分搜索(DARTS)
  8. NANK南卡降噪耳机和OPPO蓝牙耳机哪个更好呢?哪款更能打?
  9. 蚂蚁区块链BaaS平台应用开发指南(五):JS SDK的接入
  10. 微信授权绑定手机号 java_微信小程序获取手机号授权用户登录功能