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

关于Google Apps Script中UrlFetchApp.fetchAll配额限制的技术咨询

问题描述

我对Google Apps Script中UrlFetchApp.fetchAll的工作原理存在误解。正在开发一个插件用于批量复制模板并填充占位符,每个模板需替换约6个字段(后续可能增加)。少量模板处理正常,但处理20个左右时出现超时错误:

Exception: Service invoked too many times in a short time: urlfetch. Try Utilities.sleep(1000) between calls.

我的目标是扩展至处理200-300个模板,原以为fetchAll会将数组中的请求视为单次请求,这是否错误?我可以添加延时,但担心触发Google脚本运行时配额超时。想了解fetchAll的请求计数方式,以及它相较于fetch的优势。

代码示例
function copyTemplates(copiedTemplateIds, destinationFolderId) {
  let copyRequests = []
  copiedTemplateIds.forEach(id => {
    let title = getTitle(enteredInfo.name, DriveApp.getFileById(id).getName())
    copyRequests.push({
      "url": `https://www.googleapis.com/drive/v3/files/${id}/copy`,
      "method": "POST",
      "headers": {
        "Authorization": "Bearer " + token
      },
      "payload": `{"parents":"${destinationFolderId}","name":"${title}"}`,
      "contentType": "application/json",
    })
  })

  let responses = UrlFetchApp.fetchAll(copyRequests);
  responses.forEach(response => {
    copiedTemplateIds.push(JSON.parse(response.getContentText()).id)
  });

  return copiedTemplateIds;
}

function replaceText(copiedTemplateIds) {
  let fillRequests = []
  copiedTemplateIds.forEach(id => {
    for (let [field, data] of Object.entries(enteredInfo)) {
      let fill = "<<"+ field +">>"
      fillRequests.push({
        "url": `https://docs.googleapis.com/v1/documents/${id}:batchUpdate`,
        "method": "POST",
        "headers": {
          "Authorization": "Bearer " + token
        },
        "payload": `{"requests":[{"replaceAllText":{"replaceText":"${data}","containsText":{"text":"${fill}","matchCase":false}}}]`,
        "contentType": "application/json",
      })
    }
  })
  let responses = UrlFetchApp.fetchAll(fillRequests);

  console.log(responses.toString())
}
核心疑问
  1. fetchAll是否将数组中的所有请求视为单次请求?
  2. fetchAll的请求计数规则是什么?
  3. 相较于UrlFetchApp.fetch,fetchAll的优势在哪里?
  4. 如何优化代码以处理200-300个模板而不触发配额限制?
解答

1. fetchAll的请求计数规则

你的理解是错误的:fetchAll不会将数组中的请求视为单次请求。Google Apps Script的UrlFetch配额是按实际发起的HTTP请求数量统计的,fetchAll只是将多个请求打包批量发送,底层仍然是每个请求单独计数,和循环调用fetch的配额消耗完全一致。

2. fetchAll对比fetch的优势

虽然配额计数相同,但fetchAll有两个关键优势:

  • 连接复用:底层会复用HTTP连接,减少多次建立连接的开销,整体请求速度比循环调用fetch更快。
  • 代码简洁:无需手动循环处理请求和响应,一次性完成批量请求的发送和结果接收,代码结构更清晰。

3. 优化方案(针对200-300个模板的场景)

(1)合并Docs的批量更新请求

当前代码中,每个文档的每个字段都单独发送一次batchUpdate请求,这会导致请求数爆炸(200个模板×6个字段=1200次请求)。可以将一个文档的所有替换请求合并到同一个batchUpdate中,每个文档仅发送1次请求:

function replaceText(copiedTemplateIds) {
  let fillRequests = []
  copiedTemplateIds.forEach(id => {
    let requests = []
    for (let [field, data] of Object.entries(enteredInfo)) {
      let fill = "<<"+ field +">>"
      requests.push({
        "replaceAllText": {
          "replaceText": data,
          "containsText": {
            "text": fill,
            "matchCase": false
          }
        }
      })
    }
    // 合并当前文档的所有替换请求到一个batchUpdate
    fillRequests.push({
      "url": `https://docs.googleapis.com/v1/documents/${id}:batchUpdate`,
      "method": "POST",
      "headers": {
        "Authorization": "Bearer " + token
      },
      "payload": JSON.stringify({"requests": requests}),
      "contentType": "application/json",
    })
  })
  let responses = UrlFetchApp.fetchAll(fillRequests);
  console.log(responses.toString())
}

这样请求数直接从模板数×字段数减少到模板数,大幅降低配额消耗。

(2)分段批量处理+延时

即使合并请求后,200-300次请求仍可能触发短时间内的速率限制。可以将请求分成多个小批次,每发送一批后添加延时:

function processInBatches(requests, batchSize = 50, delayMs = 2000) {
  let responses = []
  for (let i = 0; i < requests.length; i += batchSize) {
    let batch = requests.slice(i, i + batchSize)
    responses.push(...UrlFetchApp.fetchAll(batch))
    // 最后一批无需延时
    if (i + batchSize < requests.length) {
      Utilities.sleep(delayMs)
    }
  }
  return responses
}

// 使用示例:
// let responses = processInBatches(fillRequests, 50, 2000)

(3)使用Google高级服务替代手动UrlFetch

手动调用Drive/Docs API容易出错且配额紧张,推荐使用Google提供的高级服务(DriveApp、DocumentApp),这些服务的配额与UrlFetch分开,且调用更简洁:

  • 复制文件:用DriveApp.getFileById(id).copyTo(DriveApp.getFolderById(destinationFolderId), title)替代UrlFetch请求。
  • 替换文本:用DocumentApp.openById(id)打开文档,然后批量执行replaceText方法,无需发送HTTP请求。

(4)检查并利用配额限制

Google Apps Script的UrlFetch配额为每天10000次,短时间内的速率限制约为每分钟100次。合并请求+分段处理后,200-300次请求完全在配额范围内。

内容的提问来源于stack exchange,提问作者jip101

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 01:48:27