You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现请求到达后无需等待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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:06:23