
简介本资源是一份面向Delphi开发者含初学者与进阶用户的Web前端开发实战教程聚焦TMS WEB Core框架解决传统Delphi程序员向现代跨平台Web应用开发转型的核心痛点。教程系统覆盖框架原理、环境配置Delphi社区版TMS试用版即可、项目创建含Web应用、PWA、Electron及Miletus桌面应用、Pas2JS编译机制、RTL运行时库、预处理器与命令行编译等关键技术并深入对比IntraWEB、uniGUI等主流Delphi Web框架差异辅以企业级UI设计、数据绑定与部署实践。资源为单文件PDF共1个3.84MB文档内容结构完整含历史演进、架构解析、快速入门、调试技巧及90余页实操目录模块便于按需精读与工程复用。已有5028人学习下载是掌握Delphi全栈Web开发能力的高价值入门与进阶指南。1. Delphi Web前端开发不是“用Delphi写网页”TMS WEB Core 把 Object Pascal 编译成纯 HTML/JS零二进制、免服务器端运行、一次编译全平台部署很多人第一次看到“Delphi Web前端开发”时下意识皱眉Delphi 不是做桌面软件的吗写网页不该用 JavaScript 吗——这恰恰是 TMS WEB Core 最反直觉、也最值得深挖的价值点。它不是在 Delphi 里嵌个 WebBrowser 控件也不是用 Delphi 写后端再套个前端模板而是通过一套自研的 Pascal-to-JavaScript 编译链核心是 Pas2JS把.pas文件直接翻译成标准、可读、可调试的 HTML CSS JavaScript最终产物是纯静态文件index.html、app.js、style.css连node_modules都不需要。你用 Delphi IDE 写代码按下 F9生成的不是.exe而是一整个能扔进 Nginx、Apache、GitHub Pages 甚至微信公众号后台的 Web 应用包。这意味着没有 IIS 依赖、不需 .NET Runtime、不走 ASP.NET 管道、不碰 WebAssembly 黑匣子——所有逻辑都在浏览器里跑数据靠 REST API 拉状态靠前端管理。教程里反复强调“100% WEB HTML/JS 应用程序”不是宣传话术是技术事实。它适合三类人老 Delphi 工程师想低成本切入 Web 领域、Lazarus 开发者需要跨平台轻量级 UI、以及被 Vue/React 构建流程折磨到怀疑人生的前端老兵——想回归“改完保存就能看效果”的朴素开发节奏。你不需要重学 JS 语法但必须接受“Pascal 是源码JS 是汇编”的新认知。2. TMS WEB Core 的技术底座Pas2JS 编译器、FNC 统一组件模型与 RTL 运行时库如何协同工作2.1 Pas2JS 编译器Object Pascal 到 JavaScript 的确定性翻译引擎TMS WEB Core 的核心不是封装而是翻译。它不依赖 V8 或 SpiderMonkey 引擎也不生成 WebAssembly 字节码而是用 Pascal 编写的 Pas2JS 编译器将.pas源码逐行映射为语义等价的 JavaScript。这不是简单字符串替换而是 AST 层级的语义保持for i : 0 to Length(arr) - 1 do→for (let i 0; i arr.length; i)try...except→try...catchTStringList→class TStringList { ... }。关键参数控制翻译行为pas2js -Jc -Jm -Ji -Jr -Jd -Jl -Jt -Jw -Jx MyApp.pas-Jc: 启用 CommonJS 模块适配 Node.js 环境非必需-Jm: 生成 ES6 模块推荐利于 Tree Shaking-Ji: 插入源码映射source map调试时可直接在浏览器 DevTools 里断点 Pascal 行-Jr: 启用 RTL 运行时库必选提供System,SysUtils,Classes等基础单元-Jd: 启用调试信息含变量名、作用域增大 JS 体积但调试友好-Jl: 启用 RTL 日志控制台输出TLog.Write(msg)-Jt: 启用类型检查生成typeof x number类型守卫-Jw: 启用警告如未初始化变量、无返回值函数-Jx: 启用异常堆栈追踪Error.stack包含 Pascal 调用链提示生产环境建议用-Jm -Jr -Jt关闭-Jd -Jl -Jw -Jx开发阶段务必加-Ji -Jd否则调试等于盲人摸象。Pas2JS 不是黑盒。它会为每个 Pascal 单元生成对应 JS 模块例如WebForm1.pas→WebForm1.js其中TWebForm1 class extends TWebForm定义清晰可见。你可以在 Chrome 控制台里输入TWebForm1.prototype查看所有方法或new TWebForm1()手动实例化——这说明它生成的是标准 JS 类不是闭包模拟的伪类。2.2 FNCFramework Neutral Components一套代码三端复用的 UI 组件抽象层TMS WEB Core 的 UI 组件如TWebButton,TWebEdit并非直接操作 DOM而是构建在 FNC 层之上。FNC 是一套与渲染目标无关的组件模型同一套 Pascal 接口既可驱动 WebHTML/CSS/JS、又可驱动 VCLWindows GDI、还可驱动 FMX跨平台原生控件。这种设计带来两个硬收益一是组件行为高度一致比如TWebButton.OnClick和TButton.OnClick参数签名完全相同二是未来迁移到桌面端时UI 逻辑代码几乎零修改。FNC 的核心是TFNCBaseControl基类它定义了Render,Update,Destroy三个生命周期钩子Render: 生成初始 HTML 字符串如button idbtn1 classtms-btnClick/buttonUpdate: 响应属性变更如Caption : Save→el.textContent SaveDestroy: 清理事件监听、定时器、内存引用所有TWeb*控件都继承自TFNCBaseControl因此TWebLabel.Caption修改后底层自动调用Update触发 DOM 更新无需手动innerHTML ...。这种响应式更新机制比 jQuery 手动操作更可靠又比 React Virtual DOM 更轻量——因为它是编译期确定的细粒度更新而非运行时 diff。2.3 RTLRun-Time Library让 Pascal 习惯无缝迁移到浏览器的运行时支撑传统 Delphi RTL如System,SysUtils为 Win32 设计大量依赖 Windows API。TMS WEB Core 提供了专为浏览器环境裁剪的 RTL位于TMSWEBCore\RTL\目录下。它重实现了关键单元Pascal 单元浏览器等效实现关键差异Systemsystem.jsInteger映射为 JSnumberBoolean映射为booleanstring映射为string非ArrayBufferSysUtilssysutils.jsFormatDateTime使用Intl.DateTimeFormatStrToInt调用parseIntFileExists返回false浏览器无文件系统Classesclasses.jsTList,TStringList,TMemoryStream全部基于 JS 数组/Uint8Array实现TThread被禁用浏览器无线程用setTimeout/Promise替代DateUtilsdateutils.js所有日期计算基于Date对象DaysBetween精确到毫秒注意TFileStream,TRegistry,TIniFile等依赖操作系统 API 的类在 RTL 中被标记为deprecated或直接抛出EUnsupportedOperation异常。这不是缺陷而是明确边界——TMS WEB Core 只负责前端呈现与交互IO 和持久化必须交由后端 REST API 完成。2.4 项目结构与构建流程从.dpr到dist/的完整链路一个标准 TMS WEB Core 项目目录结构如下MyWebApp/ ├── MyWebApp.dpr # 主程序入口Pascal ├── WebForm1.pas # 窗体单元含 UI 定义与事件处理 ├── DataModule1.pas # 数据模块封装 API 调用 ├── resources/ # 静态资源CSS, images, fonts │ └── style.css ├── index.html # 主页模板由 TMS 自动生成或自定义 └── build/ # 构建输出目录由 Pas2JS 生成 ├── app.js # 主应用 JS含所有单元编译结果 ├── index.html # 最终部署文件注入 app.js └── assets/ # 图片等二进制资源Base64 编码或独立文件构建流程分三步IDE 编译Delphi IDE 调用dcc32.exe编译.dpr为中间.dcu仅校验语法不生成 JSPas2JS 转译调用pas2js.exe读取.dcu和 RTL生成app.js资源打包TMS 工具自动将resources/下文件复制到build/并更新index.html中的script srcapp.js和link hrefstyle.css这个流程可完全命令行化便于 CI/CD 集成。例如 GitLab CI 中的.gitlab-ci.yml片段build-web: image: tmswebcore-builder:latest script: - pas2js -Jm -Jr -Jt -Ji -Jd MyWebApp.dpr - cp resources/style.css build/ artifacts: paths: - build/3. 从零创建第一个 TMS WEB Core 应用配置环境、新建项目、添加按钮并绑定点击事件3.1 环境准备Delphi 社区版 TMS WEB Core 试用版安装要点教程强调“只需免费 Delphi 社区版”但实操中版本兼容性是第一道坎。截至 2024 年Delphi 11 Alexandria 是当前最稳的基线版本。社区版下载地址为 Embarcadero 官网搜索 “Delphi Community Edition”安装时务必勾选Desktop Development with Delphi含 VCL/FMXWeb Development with Delphi含 WebBroker、IntraWeb 支持虽不用但依赖部分 RTLAdditional Tools含dcc32.exe,pas2js.exeTMS WEB Core 试用版需单独注册获取安装包名为TMSWEBCore_Install.exe。安装时注意三点不要勾选 “Install for all users”权限问题会导致 IDE 无法加载包安装路径避免中文和空格如C:\TMS\WEB\Core安全C:\Program Files\TMS或D:\我的项目\TMS会失败安装后重启 Delphi IDE否则组件面板不显示TWeb*控件验证是否成功打开 Delphi →Component→Install Packages→ 查看已加载列表中是否有TMSWEBCore.bpl版本号如v5.2.0.0。若无手动点击Add定位到C:\TMS\WEB\Core\BPL\Win32\TMSWEBCore.bpl。3.2 创建新项目选择模板、设置输出路径、理解TWebApplication生命周期启动 Delphi →File→New→Other→TMS WEB Core→Web Application。向导中关键选项Application Type: 选HTML Application非Electron或PWA先跑通基础Output Directory: 设为项目根目录下的build/与教程一致避免路径混乱Use RTL: 勾选启用 TMS RTL否则TStringList等不可用向导生成三个文件MyWebApp.dpr,WebForm1.pas,index.html。打开MyWebApp.dpr核心代码如下program MyWebApp; uses System.StartUpCopy, Web.WebApp, WebForm1 in WebForm1.pas {WebForm1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TWebForm1, WebForm1); Application.Run; end.这里Application是TWebApplication实例其Run方法不进入消息循环而是解析 URL 参数如?themedark加载index.html到 DOM实例化TWebForm1并调用WebForm1.Show启动事件轮询requestAnimationFrame驱动TWebForm1继承自TWebForm其OnCreate事件在窗体实例化后、Show前触发是初始化数据的最佳位置。3.3 添加 UI 控件拖拽TWebButton并编写OnClick事件处理器在WebForm1设计视图中从组件面板TMS WEB Core页签拖一个TWebButton到窗体上。默认Name为WebButton1Caption为Button1。双击按钮IDE 自动生成事件框架procedure TWebForm1.WebButton1Click(Sender: TObject); begin // TODO: Add your code here end;现在写一段真实可用的逻辑点击后弹出浏览器原生提示框并修改按钮文字procedure TWebForm1.WebButton1Click(Sender: TObject); begin // 调用浏览器 alert注意这是 JS alert非 Delphi MessageDlg WebForm1.Window.Alert(Hello from Delphi!); // 修改按钮 Caption自动触发 UpdateDOM 实时更新 WebButton1.Caption : Clicked!; // 记录到浏览器控制台方便调试 TLog.Write(Button clicked at DateTimeToStr(Now)); end;关键点解析WebForm1.Window.Alert是TWebWindow类的方法直接调用window.alert()非阻塞主线程与 JS 一致WebButton1.Caption : ...触发TWebButton.Update内部执行el.textContent value无需手动 DOM 操作TLog.Write输出到浏览器console.log且带时间戳比WriteLn更适合 Web 环境编译运行F9Delphi 自动打开默认浏览器地址为file:///C:/path/to/build/index.html。点击按钮弹窗出现文字变更控制台输出日志——首战告捷。3.4 配置index.html模板自定义标题、引入外部 CSS、设置 viewportindex.html是 TMS 生成的入口页但允许手动编辑。打开它你会看到类似结构!DOCTYPE html html head meta charsetutf-8 titleTMS WEB Core Application/title meta nameviewport contentwidthdevice-width, initial-scale1.0 link relstylesheet typetext/css hrefstyle.css /head body div idapp/div script typetext/javascript srcapp.js/script /body /html按需修改title改为你的应用名如title库存管理系统/titlemeta nameviewport对移动端至关重要initial-scale1.0确保不缩放user-scalableno可选禁用双指缩放link若你有自定义 CSS确保路径正确TMS 默认生成style.css在build/下无需改动div idapp这是 TMS 应用的挂载点绝对不要删除或改 ID否则TWebForm无法渲染提示若需引入第三方 CSS如 Bootstrap在head中添加link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet但注意TMS 组件有自己的 CSS 类如tms-btn与 Bootstrap 类可能冲突建议优先用 TMS 主题系统。4. TMS WEB Core 核心组件实战TWebEdit、TWebListBox与TWebDateTimePicker的数据绑定与事件处理4.1TWebEdit单行文本输入框的双向绑定与验证模式TWebEdit是最常用输入控件但新手易忽略其Text属性与OnChange事件的配合逻辑。创建一个登录表单场景用户名输入框 密码输入框 登录按钮。设计视图拖入TWebEditName : WebEditUserPlaceholder : 请输入用户名TWebEditName : WebEditPassPasswordChar : *Placeholder : 请输入密码TWebButtonName : WebButtonLoginCaption : 登录关键代码在WebButtonLoginClickprocedure TWebForm1.WebButtonLoginClick(Sender: TObject); var User, Pass: string; begin User : WebEditUser.Text; Pass : WebEditPass.Text; // 基础验证前端仅作友好提示后端必须二次校验 if Trim(User) then begin WebForm1.Window.Alert(用户名不能为空); WebEditUser.SetFocus; // 聚焦回输入框 Exit; end; if Length(Pass) 6 then begin WebForm1.Window.Alert(密码长度不能少于6位); WebEditPass.SetFocus; Exit; end; // 模拟登录请求实际应调用 REST API TLog.Write(Login attempt: User); WebButtonLogin.Caption : 登录中...; WebButtonLogin.Enabled : False; // 模拟网络延迟用 Promise 代替 setTimeout更符合现代 JS TPromise.WaitFor( function: IPromise begin Result : TPromise.New( procedure(Resolve: TPromiseResolve; Reject: TPromiseReject) begin // 此处应调用 TWebHTTPClient.Post(...) // 为简化3秒后 resolve TWebTimer.SetTimeout( procedure begin Resolve(nil); end, 3000 ); end ); end, procedure begin WebButtonLogin.Caption : 登录成功; WebButtonLogin.Enabled : True; // 跳转到主页面实际用 window.location.href WebForm1.Window.Location.Href : main.html; end, procedure(Reason: Variant) begin WebButtonLogin.Caption : 登录失败; WebButtonLogin.Enabled : True; WebForm1.Window.Alert(网络错误请重试); end ); end;这里用到两个重要工具TPromise.WaitForTMS 封装的 Promise 辅助函数避免回调地狱TWebTimer.SetTimeout浏览器setTimeout的 Pascal 封装参数为匿名方法注意TWebEdit.Text是实时同步的用户每敲一个键Text属性就更新。若需防抖Debounce不能监听OnChange它太频繁而应监听OnKeyUp并用TWebTimer.SetTimeout延迟执行。4.2TWebListBox动态加载列表项与选中事件响应TWebListBox用于显示可滚动的单选列表。假设从 REST API 加载用户列表// 在 WebForm1.OnCreate 中初始化 procedure TWebForm1.WebForm1Create(Sender: TObject); begin // 模拟从 API 获取 JSON 数据实际用 TWebHTTPClient var UsersJSON : [{id:1,name:张三},{id:2,name:李四},{id:3,name:王五}]; var Users: TJSONArray; Users : TJSONObject.ParseJSONValue(UsersJSON) as TJSONArray; // 清空现有项 WebListBox1.Clear; // 遍历 JSON添加到 ListBox for var i : 0 to Users.Size - 1 do begin var UserObj : Users.Items[i] as TJSONObject; var Name : UserObj.GetValuestring(name); var ID : UserObj.GetValueinteger(id); // AddItem 第二个参数是 Data可存任意对象如整数ID WebListBox1.AddItem(Name, Pointer(ID)); end; end;TWebListBox.AddItem的Data参数是Pointer可存Integer,TObject, 或TJSONObject。选中事件中取出procedure TWebForm1.WebListBox1Select(Sender: TObject); var SelectedIndex: Integer; SelectedID: Integer; begin SelectedIndex : WebListBox1.ItemIndex; if SelectedIndex 0 then begin // GetItemData 返回 Pointer需类型转换 SelectedID : Integer(WebListBox1.GetItemData(SelectedIndex)); TLog.Write(Selected user ID: SelectedID.ToString); // 根据 ID 加载详情调用 API LoadUserDetail(SelectedID); end; end;TWebListBox支持多选MultiSelect : True此时ItemIndex返回首个选中项需遍历Selected[]数组for var i : 0 to WebListBox1.Count - 1 do if WebListBox1.Selected[i] then TLog.Write(Selected: WebListBox1.Items[i]);4.3TWebDateTimePicker日期时间选择器的格式化与时区处理TWebDateTimePicker默认使用浏览器本地日期控件input typedatetime-local但存在兼容性问题Safari 不支持datetime-local。TMS 提供了 fallback当原生控件不可用时自动加载轻量级 JS 日期选择器。关键属性Kind:dkDate仅日期、dkTime仅时间、dkDateTime日期时间Format:dfShort,dfLong,dfCustom自定义格式如yyyy-mm-dd hh:nn:ssMinDate,MaxDate: 限制可选范围TDateTime类型// 设置为仅日期选择格式为 2024-06-15 WebDateTimePicker1.Kind : dkDate; WebDateTimePicker1.Format : dfCustom; WebDateTimePicker1.CustomFormat : yyyy-mm-dd; // 设置最小日期为今天 WebDateTimePicker1.MinDate : Now; // OnChange 事件获取选中值TDateTime procedure TWebForm1.WebDateTimePicker1Change(Sender: TObject); var SelectedDate: TDateTime; Formatted: string; begin SelectedDate : WebDateTimePicker1.Date; Formatted : FormatDateTime(yyyy-mm-dd, SelectedDate); TLog.Write(Selected date: Formatted); // 发送到后端时需转为 ISO 字符串UTC 时间 // TMS 提供 TWebDateTimeHelper.ToISO8601(SelectedDate, True) → 2024-06-15T00:00:00Z end;注意浏览器原生datetime-local输入框返回的是本地时区的字符串如2024-06-15T14:30而 REST API 通常要求 UTC 时间戳。TMS 的TWebDateTimeHelper.ToISO8601方法可自动处理时区转换第二个参数True表示转为 UTC。5. 避坑指南TMS WEB Core 开发中 5 个高频翻车现场与血泪解决方案5.1 现象编译通过但浏览器控制台报错ReferenceError: TWebForm1 is not defined原因Pas2JS 未正确生成WebForm1.js常见于WebForm1.pas单元名与文件名不一致如文件存为Form1.pas但单元声明为unit WebForm1;或WebForm1.pas未加入项目.dpr中uses未包含。解决确认WebForm1.pas文件名与unit WebForm1;完全一致大小写敏感打开.dpr文件检查uses列表是否包含WebForm1 in WebForm1.pas {WebForm1}在 Delphi IDE 中右键项目 →Options→TMS WEB Core→Build→ 勾选Verbose output重新编译查看日志中是否提示Compiling WebForm1.pas5.2 现象TWebButton点击无反应OnClick事件不触发原因TWebButton被其他 DOM 元素遮挡如TWebPanel的ZIndex过高或Enabled : False未重置或事件处理器未正确关联设计视图双击生成的事件名与.pas中声明不匹配。解决浏览器按F12→Elements标签检查按钮 DOM 元素是否被div覆盖临时删掉父容器 CSSz-index测试在OnCreate中显式设置WebButton1.Enabled : True检查.pas文件中事件声明procedure TWebForm1.WebButton1Click(Sender: TObject);必须与对象检查器Object Inspector中OnClick属性值完全一致包括大小写5.3 现象TWebEdit.Text获取不到用户输入始终为空字符串原因TWebEdit的Text属性在OnChange事件中读取是安全的但在OnKeyUp或OnKeyDown中读取可能因事件顺序导致值未更新更常见的是TWebEdit被放入TWebFrame后Text属性访问路径错误如Frame1.WebEdit1.Text未初始化。解决优先用OnChange事件处理输入完成逻辑若必须用OnKeyUp在事件末尾加WebEdit1.UpdateTextFromElement强制同步TMS 内部方法对于TWebFrame确保Frame1 : TMyFrame.Create(Self)后再访问其子控件5.4 现象REST API 调用失败TWebHTTPClient报错Network Error或CORS error原因浏览器同源策略限制前端index.html通过file://协议打开时XMLHttpRequest被禁止或后端未设置Access-Control-Allow-Origin头。解决绝不能用file://测试 API必须通过 HTTP 服务访问如http://localhost:8080/index.html。用 Python 快速起服务python3 -m http.server 8080后端需添加 CORS 头Access-Control-Allow-Origin: *开发或Access-Control-Allow-Origin: http://localhost:8080生产TMSTWebHTTPClient默认发送Content-Type: application/json若后端要求application/x-www-form-urlencoded需手动设置Request.ContentType : application/x-www-form-urlencoded;5.5 现象TWebDateTimePicker在 Safari 上不显示或日期格式错乱原因Safari 对datetime-local支持极差且Format属性在不同浏览器渲染效果不一。解决强制启用 TMS 内置日期选择器WebDateTimePicker1.UseNative : False;统一使用dfCustom格式避免dfShort各浏览器解释不同日期字符串传输一律用 ISO 8601ToISO8601前端展示用FormatDateTime绝不依赖浏览器toLocaleDateString6. 进阶技巧用TWebHTTPClient实现 RESTful 数据交互与错误重试机制6.1TWebHTTPClient核心用法GET/POST 请求、JSON 序列化与响应处理TWebHTTPClient是 TMS 封装的浏览器fetchAPI比原生XMLHttpRequest更简洁。它支持 Promise 风格避免回调嵌套。GET 请求获取用户列表procedure TWebForm1.LoadUsers; var Client: TWebHTTPClient; begin Client : TWebHTTPClient.Create; try // 配置请求头 Client.Request.Headers.Add(Accept, application/json); Client.Request.Headers.Add(Authorization, Bearer FToken); // 发送 GET 请求 Client.Get(https://api.example.com/users) .Then( procedure(Response: TWebHTTPResponse) begin if Response.StatusCode 200 then begin // 解析 JSON 响应 var JSON : TJSONObject.ParseJSONValue(Response.Content) as TJSONObject; var Users : JSON.GetValueTJSONArray(data); // 清空并填充 ListBox WebListBox1.Clear; for var i : 0 to Users.Size - 1 do begin var User : Users.Items[i] as TJSONObject; WebListBox1.AddItem(User.GetValuestring(name), User.GetValuevoid*(id)); end; end else WebForm1.Window.Alert(加载失败 Response.StatusText); end ) .Catch( procedure(Error: Variant) begin TLog.Write(GET Error: Error.ToString); WebForm1.Window.Alert(网络错误请检查连接); end ); finally Client.Free; end; end;POST 请求提交登录数据procedure TWebForm1.DoLogin(const Username, Password: string); var Client: TWebHTTPClient; Payload: TJSONObject; begin Client : TWebHTTPClient.Create; try Payload : TJSONObject.Create; try Payload.AddPair(username, Username); Payload.AddPair(password, Password); Client.Request.Headers.Add(Content-Type, application/json); Client.Post(https://api.example.com/login, Payload.ToJSON) .Then( procedure(Response: TWebHTTPResponse) begin if Response.StatusCode 200 then begin var Result : TJSONObject.ParseJSONValue(Response.Content) as TJSONObject; FToken : Result.GetValuestring(token); WebForm1.Window.Location.Href : dashboard.html; end else WebForm1.Window.Alert(登录失败 Response.StatusText); end ) .Catch( procedure(Error: Variant) begin TLog.Write(POST Error: Error.ToString); WebForm1.Window.Alert(请求超时请重试); end ); finally Payload.Free; end; finally Client.Free; end; end;6.2 错误重试机制指数退避Exponential Backoff与最大重试次数控制网络请求失败很常见硬编码Retry 3 times不够智能。TMS 没有内置重试但可用TPromise和TWebTimer实现function TWebForm1.RetryGet(URL: string; MaxRetries: Integer): IPromise; var RetryCount: Integer; Client: TWebHTTPClient; begin RetryCount : 0; function TryRequest: IPromise; begin Client : TWebHTTPClient.Create; try Result : Client.Get(URL) .Then( function(Response: TWebHTTPResponse): Variant begin if Response.StatusCode in [200, 201] then Exit(Response) // 成功结束重试 else begin Inc(RetryCount); if RetryCount MaxRetries then raise Exception.Create(Max retries exceeded: Response.StatusText); // 指数退避1s, 2s, 4s... var Delay : Round(Power(2, RetryCount - 1) * 1000); TLog.Write(Retry RetryCount.ToString , delay Delay.ToString ms); Exit(TPromise.New( procedure(Resolve: TPromiseResolve; Reject: TPromiseReject) begin TWebTimer.SetTimeout( procedure begin Resolve(TryRequest); end, Delay ); end )); end; end ) .Catch( function(Error: Variant): Variant begin Inc(RetryCount); if RetryCount MaxRetries then raise Exception.Create(Max retries exceeded: Error.ToString); var Delay : Round(Power(2, RetryCount - 1) * 1000); TLog.Write(Retry RetryCount.ToString , delay Delay.ToString ms); Exit(TPromise.New( procedure(Resolve: TPromiseResolve; Reject: TPromiseReject) begin TWebTimer.SetTimeout( procedure begin Resolve(TryRequest); end, Delay ); end )); end ); except on E: Exception do begin Client p a hrefhttps://download.csdn.net/download/jelk2888/85168258 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p