Debug/Fix "Exceeded maximum execution time" 错误 Google 插件

Debug/Fix "Exceeded maximum execution time" error Google Addon

我已经创建了我的第一个 Google 工作区插件,我已成功部署到关联的域,但由于这个错误仍在处理它。

没有错误,功能上它(曾经)运行良好,但是,我现在不断收到“超出最大执行时间”错误。

这个错误是间歇性的,令人沮丧,但它最近开始在大约 90% 以上的时间内出现,尽管之前几周没有出现过。恢复到我的代码的以前版本并没有解决问题。

关于此错误的大多数(如果不是全部)现有答案都假设脚本确实超过了脚本的时间限制(6 分钟或 30 分钟)。

要明确 - 这不是这里发生的事情。脚本不会 运行 接近允许的最长时间(据我所知)我知道)。它实际上 运行s 在给出错误之前大约 45 秒。

它可能以某种方式 运行 在后台运行并导致合法的超时错误,但我需要弄清楚是否是这种情况以及错误的原因然后修复它。

我已经联系了 Google,但不幸的是很难联系到他们的相关支持人员,他们了解这方面的知识并且可以提供帮助。

在 Cloud Logging 中针对此错误,我看到(经过混淆的 ID):

{
  "insertId": "28z8v2anf1xq6",
  "jsonPayload": {
    "context": {
      "reportLocation": {
        "filePath": "[unknown file]",
        "functionName": "[unknown function]"
      }
    },
    "message": "Exceeded maximum execution time",
    "serviceContext": {
      "service": "AKfycbx-SO5mXBNoe2leEH4wrj02t9fZZwkq5BQlJPuBaNM"
    }
  },
  "resource": {
    "type": "app_script_function",
    "labels": {
      "function_name": "fileMakerPro",
      "invocation_type": "unknown",
      "project_id": "order-management-713317"
    }
  },
  "timestamp": "2022-03-10T10:09:58.827Z",
  "severity": "ERROR",
  "labels": {
    "script.googleapis.com/process_id": "fIxbX5X5IjrIqLLK-XQgJiJV0U9vQMEAEA1GOxTqw-XzqsaVlKTNK2UcymM7feuXp-qZLshvBUg61TS6u28l_v6szoAq8QBBOvGudISVj0yceQXPKpIHO6HJ2G-uxuqy4xcnv-NzDfBTMbJH7VmK_AjZd6a5KVA-LnhtOE_28mCn_zTpI5AC3-BhX_lcCC1p-3QsMX6blhMZSYgVTTo1T_Z9SovufUWinpJieIbio-L8wzQPkjLYM2l9s5RHGuKMcT3LbvUO7fFS4DV1z_xfaR_nU1LqeayAk1aouGSXc",
    "script.googleapis.com/user_key": "wPo7ZL4DSIqpPrZfeqo7E7yAArA430YCNnzbVYhyuVlR6nKybMkISeaDZoigRDx0gAJXIjF49EoL",
    "script.googleapis.com/deployment_id": "wkq5BQlJPcbx-SO5mEHAKfywrj02t9fZZ4uBXBNoe2leaNM",
    "script.googleapis.com/project_key": "Xf9t90MIwJJbMnu2Z9hQ48TM6DPTcW9w3"
  },
  "logName": "projects/order-management-317713/logs/script.googleapis.com%2Fconsole_logs",
  "receiveTimestamp": "2022-03-10T10:09:59.313440894Z"
}

这是晦涩难懂的,对“未知数”一点帮助也没有。

它引用的函数,供参考:

async function fileMakerPro(e) {
  console.time("fileMakerPro");

  //Order Management Named Ranges
  let OM_NR = await getNamedRangesAsObject_(ss);
  
  let orderNumber, orderFolder, orderFolderName;
  let loadingDate, loadingDateFormatted, dueDate, dueDateFormatted;

  let sheetGoodsTable = OM_NR.goodsTable.ranges.filter(function (x) { /* here, x is an array, not an object */
    return !(x.every(element => element === (undefined || null || '')))
  });

  //Get order number if exists or show warning and return
  if (orderNumber = OM_NR.OrderNo.range.toString()) {

  } else {
    ui.alert(`⚠️  ORDER NUMBER \(${OM_NR.OrderNo.rangeA1.toString()}\) appears missing, please correct and try again.`);
    return;
  }
  //Get loading date if exists or show warning and return
  if (loadingDate = OM_NR.transportLoadingDate.range) {

    loadingDateFormatted = `${loadingDate.getDate()}-${loadingDate.getMonth() + 1}-${loadingDate.getFullYear()}`;
    monthName = monthNames[loadingDate.getMonth()];

    //Set Due Date 30 days after Loading Date
    dueDate = new Date(loadingDate.getFullYear(), loadingDate.getMonth(), loadingDate.getDate() + 30);
    dueDateFormatted = `${dueDate.getDate()}-${dueDate.getMonth() + 1}-${dueDate.getFullYear()}`;


  } else {
    ui.alert(`⚠️ LOADING DATE \(${OM_NR.transportLoadingDate.rangeA1.toString()}\) appears to be missing, please correct and try again.`);
    return;
  }

  let rootFolder = DriveApp.getFileById(sheetId).getParents().next();
  let tempFolder = DriveApp.getFolderById("1f4Ll-KODzmvBIuunaCS_7anMCX-w86S6");
  let monthFolders = rootFolder.getFolders();
  let tickBoxes = e.formInputs.generateDocsCheckboxes;


  let packingListTemplateId = '1cZUB4U59Z56456gdgdghhka2QQ9sUNIlQSHYy1M';
  let tickCMR = false, tickInvoice = false;

  //Find existing order folder
  let orderSearch = await searchFolders(monthFolders, monthName, orderNumber);

  //Set Order Folder Name if both values present
  orderFolderName = `${loadingDate.getDate()}-${loadingDate.getMonth() + 1}-${loadingDate.getFullYear()} \(${orderNumber}\)`;


  if (tickBoxes !== undefined) {
    if (tickBoxes.indexOf('tickBoxCMRnote') > -1) {
      tickCMR = true;
    }
    if (tickBoxes.indexOf('tickBoxInvoice') > -1) {
      tickInvoice = true;
    }
  } else {
    let confirm = ui.alert(`⚠️ No documents selected. Update Order ${orderNumber} Snapshot and Packing List only?`, Browser.Buttons.YES_NO);
    if (confirm === ui.Button.YES) {
      fileMakerPro();
    } else {
      ss.toast(`⚠️ Update cancelled.`);
      return;
    }
  }

  if (!orderSearch.foundMonthFolder) {
    ss.toast(`⚙️ Creating Month Folder...`);
    monthFolder = rootFolder.createFolder(monthName);
  } else {
    monthFolder = orderSearch.foundMonthFolder;
  }

  if (!orderSearch.foundOrderFolder) {
    ss.toast(`⚙️ Creating Order Folder...`);
    orderFolder = monthFolder.createFolder(orderFolderName);
  } else {
    if (!orderSearch.foundOrderFolder.getName().includes(loadingDateFormatted)) {
      ui.alert(`⚠️ Duplicate Order Number ${orderNumber} found: ${orderSearch.foundMonthFolder.getName()} / ${orderSearch.foundOrderFolder.getName()}.\n\n Please change Order Number or remove duplicate Order Folder.`);
      return;
    }
    orderFolder = orderSearch.foundOrderFolder;
  }


  if (tickInvoice && tickCMR) {
  
      assembleCMR(orderFolder, orderNumber, tempFolder, loadingDateFormatted, sheetGoodsTable, OM_NR);
      assembleInvoice(orderFolder, orderNumber, tempFolder, loadingDateFormatted, dueDateFormatted, sheetGoodsTable, OM_NR);

      ss.toast(`✅ Documents Saved`);


  } else {
    if (tickCMR) {

        assembleCMR(orderFolder, orderNumber, tempFolder, loadingDateFormatted, sheetGoodsTable, OM_NR);

        ss.toast(`✅ CMR Document Saved`);

    }
    if (tickInvoice) {

        assembleInvoice(orderFolder, orderNumber, tempFolder, loadingDateFormatted, dueDateFormatted, sheetGoodsTable, OM_NR);

        ss.toast(`✅ Invoice Document Saved`);

    }
  }

  let existingOrderSnapshot = orderFolder.getFilesByName(`${orderNumber} - ORDER SNAPSHOT - DO NOT EDIT`);
  let existingPackingList = orderFolder.getFilesByName(`${orderNumber} - PACKING LIST`);

  let packingListId = await createPackingList(existingPackingList, orderFolder, orderNumber, packingListTemplateId, sheetId, OM_NR);

  
  createOrderSnapshot(existingOrderSnapshot, orderFolder, orderNumber, sheetId, packingListId);

  syncOrderNumbers(orderSearch.orderNumbers);


  console.timeEnd("fileMakerPro");

  return;
}

我怀疑可能以下函数可能导致超时,因为循环可能出现故障但不确定如何检查或修复:

async function writePackingList(OM_NR, packingList, packingListId){
    let PL_NR = await getNamedRangesAsObject_(packingList);

    for (let nr in PL_NR) {
      Logger.log(nr);

      if (nr == "loadingAddress") {
        packingList.getRangeByName(`${nr}`).setValue(OM_NR[`${nr}`].range);
      } else if (nr) {
        packingList.getRangeByName(`${nr}`).setValue(OM_NR[`${nr}`].range);
      } else {
        break;
      }
    }

    ss.getRangeByName("transportTotalNetWeight").setFormula(`=IMPORTRANGE("${packingListId}", "PL!I15")`)
    ss.getRangeByName("transportTotalGrossWeight").setFormula(`=IMPORTRANGE("${packingListId}", "PL!H15")`)
}

任何帮助将不胜感激,因为我遇到了困难。我需要弄清楚如何调试此错误,了解它发生的原因并修复它,以便在完全部署时不会发生。

谢谢

编辑以包含其他功能:

//Make copy of Invoice template doc, get body, replace all placeholders, return 
async function assembleInvoice(orderFolder, orderNumber, tempFolder, loadingDateFormatted, dueDateFormatted, sheetGoodsTable, OM_NR) {
  console.time("assembleInvoice");

  ss.toast(`⚙️ Assembling Invoice Document...`);
  let prefix = orderNumber + " - ";
  let fileName = "INVOICE";
  let templateTempCopy;

  //Blue Invoice Template
  let invoiceTemplate1 = DriveApp.getFileById("C2j_q3nUP4UTUR1KiCt07r1lATPKSjzEm-EeY109T8B4");
  //Red Invoice Template
  let invoiceTemplate2 = DriveApp.getFileById("1uczJT-F-JzKzaBwh_CdhJNcFkgDHfGjypJYom4vqGIo");

  //Choose Invoice template based on Movement Type
  let staraMovementType = OM_NR.staraMovementType.range;


  if (staraMovementType.toString().includes('GB SHIPPING TO')) {
    templateTempCopy = invoiceTemplate1.makeCopy(tempFolder);
  } else {
    templateTempCopy = invoiceTemplate1.makeCopy(tempFolder);
  }


  //Open working copy
  let workingCopy = DocumentApp.openById(templateTempCopy.getId());

  //Get body from working copy
  let documentBody = workingCopy.getBody();


  //Populate table
  let allTables = documentBody.getTables();
  let invoiceGoodsTable = allTables[3];

  let itemRowTemplate = invoiceGoodsTable.getRow(invoiceGoodsTable.getNumRows() - 2);
  let totalsRowTemplate = invoiceGoodsTable.getRow(invoiceGoodsTable.getNumRows() - 1);


  for (let n in sheetGoodsTable) {
    let tableRow = invoiceGoodsTable.appendTableRow(itemRowTemplate.copy());

    tableRow.getCell(0).replaceText("{itemNo}", sheetGoodsTable[n][1]);
    tableRow.getCell(1).replaceText("{itemDesc}", sheetGoodsTable[n][0]);
    tableRow.getCell(2).replaceText("{commodityCode}", sheetGoodsTable[n][2]);
    tableRow.getCell(3).replaceText("{cartons}", sheetGoodsTable[n][4]);
    tableRow.getCell(4).replaceText("{qtyKgs}", sheetGoodsTable[n][5]);
    tableRow.getCell(5).replaceText("{price}", sheetGoodsTable[n][3]);
    tableRow.getCell(6).replaceText("{total}", sheetGoodsTable[n][5] * sheetGoodsTable[n][3]);
  }

  let totalsRow = invoiceGoodsTable.appendTableRow(totalsRowTemplate.copy());

  totalsRow.getCell(3).replaceText("{cartons}", OM_NR.transportCasesCartons.range);
  totalsRow.getCell(4).replaceText("{qtyKgs}", OM_NR.transportTotalNetWeight.range);
  totalsRow.getCell(6).replaceText("{total}", OM_NR.transportInvoiceTotal.range);


  itemRowTemplate.removeFromParent();
  totalsRowTemplate.removeFromParent();

  let importerAddress = `${OM_NR.importerConsigneeIntoEU.range}, ${OM_NR.importerAddress.range}`;

  documentBody.replaceText("{currency}", OM_NR.staraInvoiceCurrency.range);

  documentBody.replaceText("{shipToAddress}", importerAddress);

  documentBody.replaceText("{billToAddress}", importerAddress);

  documentBody.replaceText("{exporterCustomsInvoiceNo}", OM_NR.exporterCustomsInvoiceNo.range);

  documentBody.replaceText("{exporterVAT}", OM_NR.exporterVAT.range);
  documentBody.replaceText("{exporterEORI}", OM_NR.exporterEORI.range);
  documentBody.replaceText("{staraOrderNo}", OM_NR.staraOrderNo.range);
  documentBody.replaceText("{staraCustomerOrderNo}", OM_NR.staraCustomerOrderNo.range);
  documentBody.replaceText("{transportLoadingDate}", loadingDateFormatted);

  documentBody.replaceText("{dueDate}", dueDateFormatted);

  documentBody.replaceText("{paymentTerms}", "30 DAYS");
  documentBody.replaceText("{staraInvoiceNo}", OM_NR.staraInvoiceNo.range);

  documentBody.replaceText("{incoTerms}", `${OM_NR.orderIncoterm.range} ${OM_NR.orderCountryOfDestination.range}`);
  documentBody.replaceText("{transportTotalGrossWeight}", OM_NR.transportTotalGrossWeight.range);
  documentBody.replaceText("{transportTotalNetWeight}", OM_NR.transportTotalNetWeight.range);

  documentBody.replaceText("{transportTruckRef}", OM_NR.transportTruckRef.range);
  documentBody.replaceText("{transportSeal1}", OM_NR.transportSeal1.range);
  documentBody.replaceText("{transportSeal2}", OM_NR.transportSeal2.range);

  documentBody.replaceText("{transportInvoiceTotal}", OM_NR.transportInvoiceTotal.range);
  documentBody.replaceText("{staraInvoiceCurrency}", OM_NR.staraInvoiceCurrency.range);


  workingCopy.saveAndClose();


  //Gets a 'blob' of the completed document
  let completedDoc = templateTempCopy.getAs('application/pdf');

  //Check for existing PDF and move to trash (not fully delete)
  let foundFile = orderFolder.getFilesByName(prefix + fileName);
  if (foundFile.hasNext()) {
    foundFile.next().setTrashed(true);
  }

  try {
    //Create PDF
    ss.toast(` Saving ${fileName} PDF...`);

    orderFolder.createFile(completedDoc).setName(prefix + fileName);

    deleteTempFiles(templateTempCopy.getId());

  } catch (err) {
    showError(err);
  } finally {
    ss.toast(`✅ Invoice Document Saved`);
    console.timeEnd("assembleInvoice");

  }
  return;
}
async function createPackingList(existingPackingList, orderFolder, orderNumber, packingListTemplateId, sheetId, OM_NR) {
  console.time("createPackingList");

    let packingListId;
    //Get current sheet container objects and data
    if (existingPackingList.hasNext()) {
      packingListId = existingPackingList.next().getId();
    } else {
      ss.toast(` Creating Packing List...`);
      let newPackingList = DriveApp.getFileById(packingListTemplateId);
      let newPackingListCopy = newPackingList.makeCopy(`${orderNumber} - PACKING LIST`, orderFolder)
      packingListId = newPackingListCopy.getId();
    }

    let packingList = SpreadsheetApp.openById(packingListId);
    addImportrangePermission(packingListId, sheetId);

    writePackingList(OM_NR, packingList, packingListId);


    ss.toast(`✅ Packing List Created/Updated`);
    console.timeEnd("createPackingList");
    return packingListId;
}
async function createOrderSnapshot(existingOrderSnapshot, orderFolder, orderNumber, sheetId, packingListId) {
  console.time("createOrderSnapshot");

  let snapshotExists = existingOrderSnapshot.hasNext();

    //If snapshot exists, delete.
    if (snapshotExists) {
      existingOrderSnapshot.next().setTrashed(true);
      ss.toast(` Updating Order Snapshot...`);
    } else {
      ss.toast(` Creating Order Snapshot...`);
    }

    //Make copy of sheet in order folder
    let currentSheet =  DriveApp.getFileById(sheetId);
    let sheetCopy = currentSheet.makeCopy(`${orderNumber} - ORDER SNAPSHOT - DO NOT EDIT`, orderFolder);
    let sheetCopyId = sheetCopy.getId();
    let targetSpreadsheet = SpreadsheetApp.openById(sheetCopyId);
    

    //let targetSpreadsheet = SpreadsheetApp.openById(DriveApp.getFileById(sheetId).makeCopy(`${orderNumber} - ORDER SNAPSHOT - DO NOT EDIT`, orderFolder).getId());

    addImportrangePermission(targetSpreadsheet.getRangeByName("CONTROL").getValue(), sheetCopyId);
    addImportrangePermission(packingListId, sheetCopyId);

    targetSpreadsheet.getRangeByName("transportTotalNetWeight").setFormula(`=IMPORTRANGE("${packingListId}", "PL!I15")`)
    targetSpreadsheet.getRangeByName("transportTotalGrossWeight").setFormula(`=IMPORTRANGE("${packingListId}", "PL!H15")`)

    targetSpreadsheet.getRangeByName("topRow").clearDataValidations()
    targetSpreadsheet.getRangeByName("sheetBody").clearDataValidations()

    //DELETE UNNEEDED TABS FROM COPY
    targetSpreadsheet.deleteSheet(targetSpreadsheet.getSheetByName("PC"));
    targetSpreadsheet.deleteSheet(targetSpreadsheet.getSheetByName("C1"));
    targetSpreadsheet.deleteSheet(targetSpreadsheet.getSheetByName("C2"));
    targetSpreadsheet.deleteSheet(targetSpreadsheet.getSheetByName("SFC"));


    if (snapshotExists) {
      ss.toast(`✅ Order Snapshot Updated`);
    } else {
      ss.toast(`✅ Order Snapshot Created`);
    }
    console.timeEnd("createOrderSnapshot");
  
  return;
}
async function addImportrangePermission(sourceSheetId, authSheetId) {
  console.time("addImportrangePermission");
  if (sourceSheetId) {
    // donor or source spreadsheet id

    // adding permission by fetching this url
    let url = `https://docs.google.com/spreadsheets/d/${authSheetId}/externaldata/addimportrangepermissions?donorDocId=${sourceSheetId}`;

    let token = ScriptApp.getOAuthToken();

    let params = {
      method: 'post',
      headers: {
        Authorization: 'Bearer ' + token,
      },
      muteHttpExceptions: true
    };

    UrlFetchApp.fetch(url, params);
  }
  console.timeEnd("addImportrangePermission");
  return;
}

问题:

在 Workspace add-ons 中,当 Action 触发器被限制为 30 秒执行时间时执行的回调函数:

The Apps Script Card service limits callback functions to a maximum of 30 seconds of execution time. If the execution takes longer than that, your add-on UI may not update its card display properly in response to the Action.

注:

由于您没有提供与花费最多时间的功能相关的代码(例如 assembleCMRassembleInvoicecreatePackingList),我无法就此提出任何建议,但无论如何你应该尝试提高这些功能的效率,或者将它们全部拆分成几个 Actions.

参考: