Google Apps脚本超出了最大执行时间问题

时间:2020-04-03 17:20:37

标签: javascript for-loop google-apps-script optimization google-sheets

我构建了自己的应用脚本代码,以从转储文件中更新日期。 我大多数时候都面临“超出最大执行时间”的问题。但是代码工作正常。它从工作表(29 k行)导入和更新数据(16.5 K行)的状态 您可以支持使代码更快/工作正常吗?

function update_main_master() {
  var xngSs = SpreadsheetApp.openById('XXXX');
  var xngSh = xngSs.getSheetByName('XNG Clean Data');
  var MasterSs = SpreadsheetApp.openById('YYY');
  var MasterSh = MasterSs.getSheetByName('Master Sheet');
  var MasterData = MasterSh.getDataRange().getValues();
  var xngData = xngSh.getDataRange().getValues();

  //  clearFilter()
  if (MasterSh.getFilter() != null) {
    MasterSh.getFilter().remove();
  }

  xngData.splice(0, 1);
  MasterData.splice(0, 1);
  var OrderNumberMasterSh = [];
  var PathNameMasterSh = [];
  for (var i = 0; i < MasterData.length; i++) {
    OrderNumberMasterSh.push(MasterData[i][1]);
    PathNameMasterSh.push(MasterData[i][2]);
  }
  var i = 0;
  for (var x = 0; x < xngData.length && xngData[x][3] != undefined; x++) {
    var OrderNumber = xngData[x][3];
    var OrderDate = xngData[x][2];
    var PathName = xngData[x][4];
    var CustomerName = xngData[x][5];
    var MW_contractor = xngData[x][8];
    var OrderStatus = xngData[x][9];
    var OrderStage = xngData[x][10];
    var ProjectID = xngData[x][14];
    var OrderType = xngData[x][41];
    var StageDate = xngData[x][11];
    var InService = xngData[x][28];
    var RejectedReason = xngData[x][31];
    var District = xngData[x][15];
    var LinkID = xngData[x][24];
    var NewOrder = 'New Order';

    if (MW_contractor == 'A' || MW_contractor == 'B' || MW_contractor == 'C') {
      if (
        OrderType == 'New' ||
        OrderType == 'Repeater' ||
        OrderType == 'Visibility'
      ) {
        //        if(OrderType == "New" || OrderType == 'Repeater')

        var index = OrderNumberMasterSh.indexOf(OrderNumber);

        if (index == -1) {
          MasterData.push([
            OrderDate,
            OrderNumber,
            PathName,
            CustomerName,
            ProjectID,
            MW_contractor,
            OrderStatus,
            OrderStatus,
            OrderStage,
            OrderType,
            StageDate,
            InService,
            '',
            District,
            '',
            NewOrder,
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            RejectedReason,
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
            '',
          ]);
        } else {
          MasterData[index][4] = ProjectID;
          MasterData[index][5] = MW_contractor;
          MasterData[index][7] = OrderStatus;
          MasterData[index][8] = OrderStage;
          MasterData[index][10] = StageDate;
          MasterData[index][11] = InService;

          if (MasterData[index][51] == '') {
            MasterData[index][51] = LinkID;
          }

          if (
            OrderStatus == 'IN-PROCESS' ||
            OrderStatus == 'CANCELLED' ||
            OrderStatus == 'REJECTED'
          ) {
            MasterData[index][6] = OrderStatus;
          }

          if (OrderStatus == 'COMPLETED') {
            MasterData[index][6] = 'LIVE';
          }
          if (OrderStatus == 'REJECTED' && MasterData[index][48] == '') {
            MasterData[index][48] = RejectedReason;
          }
        }
      }
    }
  }

  var ContorlSS = SpreadsheetApp.openById('ZZZZ');
  var ContorlSh = ContorlSS.getSheetByName('Setup');
  ContorlSh.getRange('F6').setValue('Updated');
  ContorlSh.getRange('G6').setValue(new Date());

  MasterSh.getRange(2, 1, MasterData.length, MasterData[0].length).setValues(
    MasterData
  );
  SpreadsheetApp.flush();
}

我构建了自己的应用脚本代码,以从转储文件中更新日期。我大多数时候都遇到“超出最大执行时间”的问题。但是代码工作正常。它可以导入并更新工作表具有(29k行)的数据(16.5万行)的状态,您可以支持使代码更快/工作正常吗?

1 个答案:

答案 0 :(得分:1)

您已达到执行时间限制,对于大多数帐户来说,这是6分钟。为避免这种情况,您可以通过设置基于时间的触发器将循环分成不同的执行,该触发器将在上一个执行完成后触发每个后续执行。您将必须执行以下操作:

  • 在每次迭代后检查执行时间。如果该时间接近时间限制,则停止执行。您可以为此使用Date object

  • 在函数结尾创建以下基于时间的触发器:after(durationMilliseconds)。因此,您可以在指定的毫秒数后运行指定的任何功能。每次执行后,都会创建一个触发器来触发下一个触发器。

  • 由于要拆分循环,因此必须将循环计数器(x)存储在某个位置(可以在每次执行结束时使用PropertiesService或将其写入电子表格),并在下一个开始处进行检索,这样,在每次连续执行时,脚本都知道在哪里恢复循环。例如,如果您不知道如何存储和检索脚本属性,请参见this answer

  • 现在,您正在将所需的所有数据存储在2D数组MasterData中,并在代码末尾使用setValues将此数据写入电子表格。由于此操作仅在循环完成后才发生,因此如果将循环分成多个执行,则这是不合适的。我建议您修改代码,以便在循环内进行编写过程,并从代码末尾删除setValues。您将必须如以下示例中那样更改代码。

例如,尝试更改此内容:

MasterData[index][48] = RejectedReason;

与此:

MasterSh.getRange(index + 1, 49).setValue(RejectedReason);

这:

MasterData.push([OrderDate, OrderNumber, ...]);

与此:

MasterSh.appendRow([OrderDate, OrderNumber, ...]);

示例代码(检查内联注释):

function update_main_master() {
  var begin = new Date(); // Time when execution begins
  // Your code before loop
  var x_old = // Retrieve x stored in previous execution (from PropertiesService? Spreadsheet?) (should be 0 if first execution)
  var timeLimit = 1000 * 60 * 5; // 5 minutes (in milliseconds)
  while (new Date() - begin < timeLimit) { // Check if 5 minutes passed since execution start
    for (var x = 0; x < xngData.length && xngData[x][3] != undefined; x++) {
      // Your code inside loop
    }
  }
  // Store current x index (using PropertiesService? Or write to the spreadsheet itself?
  var ContorlSS = SpreadsheetApp.openById('ZZZZ');
  var ContorlSh = ContorlSS.getSheetByName('Setup');
  ContorlSh.getRange('F6').setValue('Updated');
  ContorlSh.getRange('G6').setValue(new Date());
  if (x < xngData.length) { // Create trigger if x hasn't reach the total number of iteration
    ScriptApp.newTrigger("update_main_master")
    .timeBased()
    .after(1000 * 60) // This fires the function 1 minute after the current execution ends. Change this time according to your preferences
    .create();  
  }
}

参考: