如何实现请求到达后无需等待Google Apps Script Web App执行完成
实现方案
Google Apps Script的doGet/doPost入口函数默认会等内部所有逻辑执行完才返回响应,要实现「接收请求立刻返回、耗时逻辑后台异步执行」的效果,不需要改Python端请求逻辑,只需要在GAS侧把同步链路里的耗时逻辑拆出来,用一次性时间触发器触发后台执行即可,具体改造如下:
改造逻辑说明
doGet入口只做3件事:校验请求参数、缓存待处理的业务参数、创建绑定耗时处理函数的一次性触发器,之后立刻返回成功响应给客户端,整个过程耗时仅几百毫秒- 触发器会在后台自动触发耗时逻辑执行,和前端请求链路完全隔离
- 客户端收到200响应即可确认请求已被服务端成功接收,不需要等待业务逻辑跑完
改造后的GAS代码
// 请求入口,立刻返回响应 function doGet(e) { // 校验必填参数,参数缺失直接返回错误,客户端第一时间可感知 const requiredParams = ['backteam', 'backodds', 'layteam', 'layodds', 'advantage']; for (const param of requiredParams) { if (!e.parameter[param]) { return ContentService.createTextOutput(JSON.stringify({ code: 400, msg: `缺少必填参数:${param}` })).setMimeType(ContentService.MimeType.JSON); } } const cache = CacheService.getScriptCache(); const taskId = Utilities.getUuid(); // 缓存请求参数,有效期10分钟足够后台任务消费 cache.put(`task_${taskId}`, JSON.stringify(e.parameter), 600); // 把任务ID加入待处理队列 const pendingTasks = JSON.parse(cache.get('pending_tasks') || '[]'); pendingTasks.push(taskId); cache.put('pending_tasks', JSON.stringify(pendingTasks), 600); // 创建一次性时间触发器,延迟60秒触发后台处理函数(GAS触发器支持的最短延迟为1分钟,实测多数场景30-60秒内即可触发) ScriptApp.newTrigger('runLongTimeTask') .timeBased() .after(60000) .create(); // 立刻返回成功响应,不等待后续逻辑 return ContentService.createTextOutput(JSON.stringify({ code: 200, msg: '请求已接收,任务后台处理中', taskId: taskId })).setMimeType(ContentService.MimeType.JSON); } // 后台执行的耗时逻辑 function runLongTimeTask() { const lock = LockService.getDocumentLock(); // 加锁避免并发写入冲突 if (lock.tryLock(30000)) { try { // 先删除当前已触发的一次性触发器,避免重复执行 const allTriggers = ScriptApp.getProjectTriggers(); for (const trigger of allTriggers) { if (trigger.getHandlerFunction() === 'runLongTimeTask') { ScriptApp.deleteTrigger(trigger); } } const cache = CacheService.getScriptCache(); const pendingTasks = JSON.parse(cache.get('pending_tasks') || '[]'); if (pendingTasks.length === 0) return; // 逐个处理队列里的任务 for (const taskId of pendingTasks) { const taskDataCache = cache.get(`task_${taskId}`); if (!taskDataCache) continue; const taskData = JSON.parse(taskDataCache); // 原有业务逻辑 Utilities.sleep(30000); const targetSheet = SpreadsheetApp.openById('XXXXXXX'); const targetPage = targetSheet.getSheetByName('STACKTEST'); let writeRow = 1; while (targetPage.getRange(writeRow, 1).getValue()) { writeRow++; } targetPage.getRange(writeRow, 1, 1, 5).setValues([[ taskData.backteam, taskData.backodds, taskData.layteam, taskData.layodds, taskData.advantage ]]); // 处理完删除对应任务缓存 cache.remove(`task_${taskId}`); } // 清空待处理队列 cache.remove('pending_tasks'); } catch (e) { // 建议保留错误日志,方便排查问题 console.error('后台任务执行失败:', e); } finally { lock.releaseLock(); } } }
注意事项
- 改造后Python端不需要做特殊调整,发起请求后会立刻收到接口响应,判断返回的code为200即可确认请求已被服务端接收,之后可以直接执行后续其他逻辑,不需要设置强制超时
- 不要在
doGet里持有文档锁,避免阻塞其他请求,锁逻辑放在后台任务函数里即可 - 如果有查询任务执行结果的需求,可以额外加一个查询接口,根据返回的taskId读取缓存/表格里的任务状态即可
内容的提问来源于stack exchange,提问作者Digital Farmer
相关产品推荐
相关产品推荐

