我正在为 Shopee API 开发一个 Wrapper 作为 Delphi 包,它是开源的,可以在我的 gh 个人资料中找到。我已经遇到了与此类似的问题,该问题已在here得到解答。但现在我已经看不到路了。
从这里开始:
constructor TShopeeContext.Create(AMode, AAPIKey, APartnerID: string);
begin
if (AMode <> TEST_SHOPEE) and (AMode <> PRODUCTION_SHOPEE_BR) then
raise Exception.Create('Mode Invalid for Context');
FHost := AMode;
FAPI_Key := AAPIKey;
FPartnerID := APartnerID;
UpdateInternalConfigFields;
if FRefreshToken = '' then
FHasRefreshToken := False
else
FHasRefreshToken := True;
// Checks if the authorization has been granted and is younger than 365 days.
if (not FAuthorizationGranted) or ((Now - FAuthorizationDate) > 365) then
begin
Authorize;
end;
end;
在
Authorize;
调用之后,任何依赖于授权代码的指令都会陷入未定义行为,因为无法保证我有代码。我需要等待授权返回才能继续。我的问题源于这样一个事实:Authorize 创建一个对象并启动一个异步例程,一个 HTTP 侦听器:
procedure TShopeeContext.Authorize;
var
Authorizator: TShopeeAuthorizator;
begin
// Request Authorization;
Authorizator := TShopeeAuthorizator.Create(FHost, FAPI_Key, FPartnerID, 8342);
try
Authorizator.AuthorizationRequest;
finally
Authorizator.Free;
end;
end;
AuthorizationRequest
程序的实现如下:
procedure TShopeeAuthorizator.AuthorizationRequest;
begin
// Obtem a assinatura da chamada
FSignature := GetPublicSignString(API_PATH_AUTHORIZATION, FTimestamp);
// Constroi os parametros
FRequestParameters.AddItem('partner_id', FPartnerID);
FRequestParameters.AddItem('timestamp', FTimeStamp);
FRequestParameters.AddItem('sign', FSignature);
FRequestParameters.AddItem('redirect', REDIRECT_URL_WS+':'+IntToStr(FPort));
ShellExecute(0, 'open', PChar(GenerateRequestURL(API_PATH_AUTHORIZATION, FRequestParameters)), nil, nil, SW_SHOWNORMAL);
FCatcherServer := TCatcherServer.Create();
FCatcherServer.OnFieldsReady := FieldsReadyHandler;
FCatcherServer.Listen(FPort);
end;
和
FieldsReadyHandler
是我旧问题的主题,当前版本是:
procedure TShopeeAuthorizator.FieldsReadyHandler(Sender: TObject; Code,
AuthorizatedID: string);
begin
// Handle Code, Auth Type and AuthorizatedID.
FConfigurator := TConfiguratorFile.Create;
try
FConfigurator.SaveAuthorizationInfo(Code, AuthorizatedID, (Sender as TCatcherServer).AuthorizationType);
FSuccess := True;
finally
FConfigurator.Free;
end;
TThread.Queue(nil, procedure
begin
Sender.Free;
end);
end;
通常这是如何做到这一点的问题,但老实说,在这一点上,我认为我把它复杂化了,甚至不知道我应该做什么来克服这个问题。无论如何,如果我要重写,我可以看到我自己陷入了同样的位置,我的后续任务取决于异步任务来完成。我还尝试添加一个事件来通知上下文它已经获得了代码,但是(我的观点是)由于对象的释放方式,它会引发访问冲突。
抱歉,我无法为此综合一个 TL;DR,但如果我必须索引我的问题,输出将是:解决此类问题的最佳方法是什么以及如何在 pascal 中处理它?例如,在 JS 中,我会无限链接
.then()
直到例程不再需要异步部分。我还觉得如果我现在不学会处理它,它会成为一种常见的痛苦,因为请求是异步的。
提前致谢。
参考:等待任务完成
为此,请使用事件对象。事件对象 (System.SyncObjs.TEvent) 应在全局范围内创建,以便 它们可以像所有线程都可见的信号一样起作用。 当一个线程完成其他线程所依赖的操作时,它 调用 TEvent.SetEvent。 SetEvent 打开信号,因此任何其他 检查的线程将知道操作已完成。
授权完成后,设置事件:
procedure TShopeeAuthorizator.FieldsReadyHandler(Sender: TObject; Code,
AuthorizatedID: string);
begin
// Handle Code, Auth Type and AuthorizatedID.
FConfigurator := TConfiguratorFile.Create;
try
FConfigurator.SaveAuthorizationInfo(Code, AuthorizatedID, (Sender as TCatcherServer).AuthorizationType);
FSuccess := True;
// Notify
Event1.SetEvent;
finally
FConfigurator.Free;
end;
TThread.Queue(nil, procedure
begin
Sender.Free;
end);
end;
并在 TShopeeContext 中,使用 Event 等待授权完成,或超过给定的超时时间:
if (not FAuthorizationGranted) or ((Now - FAuthorizationDate) > 365) then
begin
Event1.ResetEvent; { clear the event before }
Authorize;
if Event1.WaitFor(20000) <> wrSignaled then
raise Exception;
end;
备注: