Google apps script 带超时保护的循环,只运行一次;正如它应该的那样;
我编写了一个脚本,可以从电子表格中的备份中恢复日历数据。由于数据量高度不可预测,我设计了一个循环,在给定的分钟数后停止,要求用户在显示实际计数器状态时Google apps script 带超时保护的循环,只运行一次;正如它应该的那样;,google-apps-script,Google Apps Script,我编写了一个脚本,可以从电子表格中的备份中恢复日历数据。由于数据量高度不可预测,我设计了一个循环,在给定的分钟数后停止,要求用户在显示实际计数器状态时继续或取消(这是为了防止Google max execution time limit出现问题) 它工作得很好,但在我用来测试这个想法的简化测试脚本中,它只工作一次:当第一个“超时”发生时,它按预期显示continue/cancel选项,然后从开始的位置继续,但是当第二次出现相同情况时,continue按钮不显示 我的问题很简单:为什么?或者更好:
继续
或取消
(这是为了防止Google max execution time limit出现问题)
它工作得很好,但在我用来测试这个想法的简化测试脚本中,它只工作一次:当第一个“超时”发生时,它按预期显示continue/cancel
选项,然后从开始的位置继续,但是当第二次出现相同情况时,continue
按钮不显示
我的问题很简单:为什么?或者更好:两种情况之间有什么区别
嵌入脚本的电子表格是(请参见菜单:测试)
整个脚本如下图所示(当然有点长,但interesting部分已接近尾声)
我使用ScriptProperties
跟踪执行时间,并从我离开的地方继续循环
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{name: "test", functionName: "test"},
];
ss.addMenu("test", menuEntries);
}
function test(){
ScriptProperties.setProperty('restorePointers',[0,0].join('@'))
var app = UiApp.createApplication().setTitle("test");
app.setHeight(150).setWidth(250);
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel();
var handlerCancel = app.createServerHandler('canceltest');
var handlerContinue = app.createServerHandler('continuetest');
var contCHandler = app.createClientHandler();
var cancel = app.createButton("cancel.", handlerCancel).setId('cancel').setVisible(false);
var cont = app.createButton('continue',handlerContinue).setId('continue').setVisible(false).addClickHandler(contCHandler);
var button = app.createButton('start').setId('button');
var handler = app.createServerClickHandler('runtest');
handler.addCallbackElement(panel);
contCHandler.forTargets(button).setEnabled(false).forEventSource().setVisible(false);
var cHandler = app.createClientHandler().forTargets(cancel).setVisible(true).forEventSource().setVisible(false);
button.addClickHandler(handler).addClickHandler(cHandler);
app.add(panel.add(button).add(cont).add(cancel))//.add(trig));
doc.show(app);
}
function canceltest(e){
var app = UiApp.getActiveApplication();
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers','canceled');
SpreadsheetApp.getActiveSpreadsheet().toast(' ','restore aborted');
app.close()
return app;
}
function continuetest(e){
runtest(e)
}
function runtest(e){
var dStart; var dEnd;
ScriptProperties.setProperty('startrestore',new Date().getTime().toString())
if(ScriptProperties.getProperty('restoreData')==null||Utilities.jsonStringify(ScriptProperties.getProperties()).indexOf('restoreData')==-1)
{ScriptProperties.setProperty('restoreData',Utilities.jsonStringify(e))
}
var app = UiApp.getActiveApplication();
var pointers = ScriptProperties.getProperty('restorePointers');
if(pointers=='0@0'){
dStart = 0;
dEnd = 500;
}else{
dStart = Number(pointers.split('@')[0]);
dEnd = Number(pointers.split('@')[1]);
}
// main loop --------------------------
for(var ee=dStart;ee<dEnd;++ee){ // main loop
var ccc = ScriptProperties.getProperty('restorePointers');
if(ccc=='canceled'){ app.close();return app};
Utilities.sleep(85); // simulate some activity
if((ee/10)==parseInt(ee/10)&&ee>0){
SpreadsheetApp.getActiveSpreadsheet().toast(ee+' steps completed')
if(new Date().getTime()-Number(ScriptProperties.getProperty('startrestore'))>12000){ ;// +- 12 sec timeout
ScriptProperties.setProperty('restorePointers',[ee,dEnd].join('@'));
app.getElementById('continue').setHTML('continue from '+ee).setVisible(true)
return app
}
}
}
// end of main loop-----------------
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers',0+'@'+0);
SpreadsheetApp.getActiveSpreadsheet().toast('normal process end');
app.close();
return app;
}
函数onOpen(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
变量菜单项=[
{name:“test”,functionName:“test”},
];
ss.添加菜单(“测试”,菜单);
}
功能测试(){
ScriptProperties.setProperty('restorePointers',[0,0]。连接('@'))
var app=UiApp.createApplication().setTitle(“测试”);
安装高度(150)、安装宽度(250);
var doc=SpreadsheetApp.getActiveSpreadsheet();
var panel=app.createVerticalPanel();
var handlerCancel=app.createServerHandler('canceltest');
var handlerContinue=app.createServerHandler('continuetest');
var contCHandler=app.createClientHandler();
var cancel=app.createButton(“cancel.”,handlerCancel).setId(“cancel”).setVisible(false);
var cont=app.createButton('continue',handlerContinue.).setId('continue').setVisible(false).addClickHandler(contCHandler);
var button=app.createButton('start').setId('button');
var handler=app.createServerClickHandler('runtest');
handler.addCallbackElement(面板);
contCHandler.forTargets(按钮).setEnabled(错误).forEventSource().setVisible(错误);
var cHandler=app.createClientHandler().forTargets(取消).setVisible(真).forEventSource().setVisible(假);
addClickHandler(handler).addClickHandler(cHandler);
app.add(面板添加(按钮).add(继续).add(取消))/.add(触发));
文件显示(应用程序);
}
功能测试(e){
var app=UiApp.getActiveApplication();
ScriptProperties.setProperty('restoreData','')
setProperty('restorePointers','cancelled');
SpreadsheetApp.getActiveSpreadsheet().toast(“”,'restore aborted');
app.close()
返回应用程序;
}
函数连续测试(e){
运行测试(e)
}
函数运行测试(e){
var dStart;var dEnd;
ScriptProperties.setProperty('startrestore',new Date().getTime().toString())
if(ScriptProperties.getProperty('restoreData')==null | | | Utilities.jsonStringify(ScriptProperties.getProperties()).indexOf('restoreData')==1)
{ScriptProperties.setProperty('restoreData',Utilities.jsonStringify(e))
}
var app=UiApp.getActiveApplication();
var pointers=ScriptProperties.getProperty('restorePointers');
如果(指针=='0@0'){
dStart=0;
dEnd=500;
}否则{
dStart=Number(指针.split('@')[0]);
dEnd=编号(指针.split('@')[1]);
}
//主回路--------------------------
对于(var ee=dStart;ee0){
SpreadsheetApp.getActiveSpreadsheet().toast(ee+“步骤完成”)
如果(new Date().getTime()-Number(ScriptProperties.getProperty('startrestore'))>12000){;//+-12秒超时
ScriptProperties.setProperty('restorePointers',[ee,dEnd].join('@');
app.getElementById('continue').setHTML('continue from'+ee.).setVisible(true)
返回应用程序
}
}
}
//主回路结束------------------
ScriptProperties.setProperty('restoreData','')
setproperties('restorePointers',0+'@'+0);
SpreadsheetApp.getActiveSpreadsheet().toast('正常进程结束');
app.close();
返回应用程序;
}
以下是阻止“继续”按钮每次更新的原因。您的服务器处理程序需要返回一个应用程序:
function continuetest(e){
return runtest(e) ///<<<
}
每次暂停后,ee的值都会在toast中重复。这可以通过记住最后显示的值并跳过它来修复
if (ee == Number(ScriptProperties.getProperty('lastToast'))) continue; ///<<< don't repeat toast
ScriptProperties.setProperty('lastToast',ee); ///<<<
如果(ee==Number(ScriptProperties.getProperty('lastToast'))继续///我遇到了一个类似的事件,虽然我的函数与按钮完全相关,但函数只触发一次。我希望我们能尽快解决。我怀疑这与return应用程序杀死最初设置的任何处理程序有关。像往常一样,很好的答案:-)没有考虑从continue返回函数本身。。。也谢谢你的想法。这可能是我的“业余”JS编程方法的症状,我除了自己尝试/实验之外从未学过任何东西,所以你的答案给了我学习更好、更“学术”的机会。谢谢。我玩得很开心,也从中学到了很多。我自己也没做过那么多。
if (ee == Number(ScriptProperties.getProperty('lastToast'))) continue; ///<<< don't repeat toast
ScriptProperties.setProperty('lastToast',ee); ///<<<
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{name: "test", functionName: "test"},
];
ss.addMenu("test", menuEntries);
}
function test(){
ScriptProperties.setProperty('restorePointers',[0,0].join('@'))
var app = UiApp.createApplication().setTitle("test");
app.setHeight(150).setWidth(250);
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel();
var handlerCancel = app.createServerHandler('canceltest');
var handlerContinue = app.createServerHandler('continuetest');
var contCHandler = app.createClientHandler();
var cancel = app.createButton("cancel.", handlerCancel).setId('cancel').setVisible(false);
var cont = app.createButton('continue',handlerContinue).setId('continue').setVisible(false).addClickHandler(contCHandler);
var start = app.createButton('start').setId('start');
var handler = app.createServerClickHandler('runtest');
handler.addCallbackElement(panel);
contCHandler.forTargets(start).setEnabled(false).forEventSource().setVisible(false);
var cHandler = app.createClientHandler().forTargets(cancel).setVisible(true).forEventSource().setVisible(false);
start.addClickHandler(handler).addClickHandler(cHandler);
app.add(panel.add(start).add(cont).add(cancel))//.add(trig));
doc.show(app);
}
function canceltest(e){
var app = UiApp.getActiveApplication();
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers','canceled');
SpreadsheetApp.getActiveSpreadsheet().toast(' ','restore aborted');
app.close()
return app;
}
function continuetest(e){
return runtest(e) ///<<<
}
function runtest(e){
var dStart; var dEnd;
ScriptProperties.setProperty('startrestore',new Date().getTime().toString())
if(ScriptProperties.getProperty('restoreData')==null||Utilities.jsonStringify(ScriptProperties.getProperties()).indexOf('restoreData')==-1)
{ScriptProperties.setProperty('restoreData',Utilities.jsonStringify(e))
}
var app = UiApp.getActiveApplication();
var pointers = ScriptProperties.getProperty('restorePointers');
if(pointers=='0@0'){
dStart = 0;
dEnd = 500;
}else{
dStart = Number(pointers.split('@')[0]);
dEnd = Number(pointers.split('@')[1]);
}
// main loop --------------------------
for(var ee=dStart;ee<dEnd;++ee){ // main loop
var ccc = ScriptProperties.getProperty('restorePointers');
if(ccc=='canceled'){ app.close();return app};
Utilities.sleep(85); // simulate some activity
if((ee%10==0)&&ee>0){ ///<<< modulus
if (ee == Number(ScriptProperties.getProperty('lastToast'))) continue; ///<<< don't repeat toast
ScriptProperties.setProperty('lastToast',ee); ///<<<
SpreadsheetApp.getActiveSpreadsheet().toast(ee+' steps completed')
if(new Date().getTime()-Number(ScriptProperties.getProperty('startrestore'))>12000) { // +- 12 sec timeout
ScriptProperties.setProperty('restorePointers',[ee,dEnd].join('@'));
app.getElementById('continue').setHTML('continue from '+ee).setVisible(true)
return app
}
}
}
// end of main loop-----------------
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers',0+'@'+0);
SpreadsheetApp.getActiveSpreadsheet().toast('normal process end');
app.close();
return app;
}