【问题标题】:Script stopped working. I turned off sumbissions & re-enabled later. now script works again, is it too instensive for google?脚本停止工作。我关闭了sumbissions并稍后重新启用。现在脚本又可以工作了,对谷歌来说是不是太密集了?
【发布时间】:2016-02-20 12:53:28
【问题描述】:

我为谷歌创建了一个在提交表单上运行的脚本。 它需要一个在表单中输入的数字,然后在工作表中查找它。 如果找到匹配项,则会在提交表单中输入时间戳。

在这里

// this is the name of our script, myFunction can be anything.
// e is a special variable, its the form & answers that has 
// just been submitted by a user.

function myFunction(e) { 


    var sessionDate = e.values[0]; // This gets the timestamp of the submission
    var sessionID = e.values[1]; // This gets the session ID the user entered


    var ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the spreadsheet where the form is attached to and all sheets are stored
    var sheet = ss.getSheetByName("Data Dump"); // Specifies the data dump sheet, which has the IDs and the cell we want to add the timestamp to
    var range = sheet.getRange("B:B"); // Specifies the column that holds the session IDs, in the data dump sheet
    var values = range.getValues(); // Reads the data in the sessions ID column, that we specified earlier

    for(i=0;i<values.length;i++){ // This goes through the session ids one by one, and we can test each one


        if (values[i][0] == sessionID){ // This is the test. If the ID we are currently looking at matches the ID on the form response, then lets do something

            sheet.getRange(i+1, 7).setValue(sessionDate); // This is us doing that something. Using where we found the matching ID, lets write the timestamp in a cell.
            break; // This stops us going through anymore ID's as we have already found the one we are looking for

        }
    }

}

经过反复测试,效果很好。然后它被设置为上线。 与很多人共享表单。

有很多提交,然后脚本停止工作。即使在手动测试时,我也没有收到任何具体错误。只是电子表格服务错误。

所以我在一两天前就停止了对表单的回复。今天是我必须查看的第一个更改,并且一切正常。脚本在几秒钟内运行。

我唯一能想到的是脚本对于谷歌来说太密集了,需要多次提交,我特别想for 循环可能会给它带来问题。

有没有人有任何想法或任何我可以尝试的?

提前致谢

【问题讨论】:

  • 可能是用户提交了无效的 sessionID,数据转储表的行数过多。
  • 有人能解释一下否决票吗?在之前的问题反馈之后。我已经确保我注释了我的代码,并试图很好地布局它。 (感谢您的编辑)。试图确保问题和解释简明扼要。因此,如果您有任何其他反馈来提高未来的问题质量,我们将不胜感激。
  • @Rubén 无效的会话 ID 很重要。因为我们只在匹配成功时才做事。如果没有匹配,则什么也不做。我已经提交了 ID 不匹配的回复,但它仍在运行。我将研究太多行的想法。谢谢
  • 我怀疑反对意见来自 JavaScript 论坛,当他们看到不是“纯”编码问题的帖子时,那里的一些人有点咄咄逼人......但我没有足够的声誉看到那种信息,所以这只是基于经验的怀疑;-)
  • 实际上,您不需要它来格式化您的帖子(您可以随时删除)。顺便说一句,我赞成补偿(几乎;-))

标签: google-apps-script google-sheets google-forms


【解决方案1】:

对于表单提交,您应该始终使用LockService。因为互联网可能连接不好,或者谷歌的服务器可能很慢等等。你应该在获取锁时尝试/捕获:

try {
  var lock = LockService.getScriptLock();
  var isLockt = lock.tryLock(10000);
catch (err) {
  Utilities.sleep(2000);//Wait 2 seconds
  lock = LockService.getScriptLock();//Try again
  isLockt = lock.tryLock(10000);
};

if (!isLockt) { // Not true
  //Email developer.  There was an error
  //Email user.  There was an error.
  //Keep running code?  Or quit?
  return;
};

您无需遍历您的价值观。有一个更快的方法。你只得到一列数据,所以你不需要二维数组。获取二维数组并将其展平为一维。

var values = range.getValues();
values = values.toString().split(",");//Convert 2D to 1D

var indexOfSessionID = values.indexOf(sessionID);//Gets index of first
     //occurrence of the sessionID

这为您提供了数据中第一次出现 sessionID 的索引。数据是零索引的,因此索引和行号不同步。索引零是数组中的第一个元素。如果范围从第一行开始,则要获得正确的行号,请将 1 添加到索引。如果范围从第二行开始,则添加 2。

如果有多个会话 ID,indexOf() 将只获得第一个。但是你的 for 循环在获取会话 ID 后会中断,所以我假设没关系。

另外,我会改变这一行:

var range = sheet.getRange("B:B");

到:

var range = sheet.getRange(1,2,sheet.getLastRow());

从第 1 行第 2 列开始,并获取到最后一行的行数。

【讨论】:

    【解决方案2】:

    在循环中输入range.offset(i, 7).setValue(sessionDate);,而不是sheet.getRange(i+1, 7).setValue(sessionDate);

    请看这里:https://developers.google.com/apps-script/reference/spreadsheet/range#offsetrowoffset-columnoffset

    但是,如果提交了多个表单,您可能会同时调用脚本,因此锁定脚本或节省时间或对表单进行排队可能会有所帮助。

    使用锁(参见:https://developers.google.com/apps-script/reference/lock/lock#tryLock(Integer)

    节省时间/队列表单的更复杂的解决方案:
    尝试完全不同的策略。
    例如:不是在表单提交触发器时调用myFunction(e),而是存储所有到达PropertiesService.getScriptProperties().setPropertye.values,同时创建一个定时触发器以在一分钟内运行myFunction,从@987654332检索所有存储的e.values @ 然后将它们全部处理成一个块。同时,任何新的e.Values 都会存储在PropertiesService 中(参见https://developers.google.com/apps-script/reference/script/clock-trigger-builder#after(Integer)

    我意识到这些不是简单的解决方案。如果您对它们感兴趣,我可以尝试帮助编写脚本。我不是专家,但我会尝试使用这些选项。

    【讨论】:

    • 感谢您的回复和建议。我以前用过offset,但是以编程方式,为什么它比当前行更好?我从未使用过 lock 或 PropertiesService,但我很想看看这个解决方案,以及它是否有助于脚本运行。如果您有任何空闲时间,我会很感激您的想法和意见。
    • 我不确定,但我认为 offset 更快,因为它不会创建或寻找新的范围。但是,在您的情况下,我认为您不会在该循环中多次写入工作表,是吗?我认为您的意图是仅将一种表单值纳入脚本;然后使用循环找到这个值。你能告诉我,B:B 中有多少个值……它们几乎是固定的吗?一会儿我会考虑写一些脚本。
    • 是的,会话 ID 是唯一的,在脚本的迭代中只写入一个值,然后结束。在 B:B 中,它们可能是 100 个或 1000 个 ID。但是当脚本停止工作时,我清除了大多数值。我总是可以重新添加这些数据。也许将 B:B 数组展平为一维数组,然后尝试 indexOf() 可能会不那么密集。但我喜欢 lockscript 的想法,这意味着提交中的另一个不会触发脚本,直到当前一个完成。感谢@DavidTew 的所有帮助
    • 好的 - 看起来你不在了:TBH 我只会搜索:stackoverflow.com/search?q=LockService.getScriptLock%28%29%3B,然后从这些结果中收集一些想法。所以我不会假装是锁服务专家。建议:你让它充分发挥作用;然后在这里自己创建一个答案;然后选择您自己的回答作为最佳答案。这将完成此查询,为我节省大量时间!并给你积分!
    【解决方案3】:

    所以经过大量思考和@David Tew 的一些改变游戏规则的建议后,我认为这就是解决方案。

     // this is the name of our script, myFunction can be anything. e is a special variable, its the form & answers that has just been submitted by a user.
     function myFunction(e) {
    
    
         var lock = LockService.getScriptLock();
    
         lock.waitLock(10000);
    
    
    
    
         var sessionDate = e.values[0]; // This gets the timestamp of the submission
         var sessionID = e.values[1]; // This gets the session ID the user entered
    
    
         var ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the spreadsheet where the form is attached to and all sheets are stored
         var sheet = ss.getSheetByName("Data Dump"); // Specifies the data dump sheet, which has the IDs and the cell we want to add the timestamp to
         var range = sheet.getRange(1, 2, sheet.getLastRow()); // Specifies the column that holds the session IDs, in the data dump sheet
         var values = range.getValues(); // Reads the data in the sessions ID column, that we specified earlier
    
         // Edits below
         values = values.toString().split(","); //Convert 2D to 1D
    
         var indexOfSessionID = values.indexOf(sessionID); //Gets index of first
         //occurrence of the sessionID
    
         if (indexOfSessionID > 0) {
             sheet.getRange(indexOfSessionID + 1, 7).setValue(sessionDate);
         }
    
    
         SpreadsheetApp.flush();
         lock.releaseLock();
     }
    

    我现在正在与多个用户一起测试它。 桑迪 - 感谢平面数组的想法,但是当我对大卫发表评论时,它已经让我印象深刻了,但是复制和粘贴代码。嗯。谢谢。

    虽然我已经逐行发布了我自己问题的答案(我认为)。 我认为 David 向我指出的信息必须是最佳答案,因为它迫使我重新审视我制作的旧脚本并改变我将来使用事件驱动触发器的方式。也可以表明您正在努力解决自己的问题并避免投反对票。

    我恳请任何编写具有事件驱动触发器(例如 onEdit、onFormsubmit)的脚本的人,并且有多个用户触发该触发器,甚至阅读 David 共享的链接。

    GAS Class Lock

    在下面编辑

    4 个不同的谷歌用户提交了 5 个回复,每个回复的时间为 2 分 10 秒,结果都奏效了。明天要做一个更大的测试。

    【讨论】:

    • @SandyGood 感谢您对 try/catch 的更新响应。我将不得不使用它,看看我是否可以将它合并到我的代码中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多