我正在开发一种可以可靠地执行单个或多个函数的方法。我已经取得了相当的成功,但是确保调用堆栈完全按批次执行时存在一些问题。该理论仍然不是防水的,但根据我的估计,它应该比它更好。
有3个功能(有一些多余的功能可以删除以进行测试):
asyncFunctionLaunch - 在线调用以将新函数集(key)引入调用堆栈。每个键可以包含一个或多个要调用的函数,每个函数都有一个属性“start”,用于标记函数已被调用,但可能尚未完成执行。为了获得更好的想法,使用getTime()的派生来分配密钥。测试显示偏移添加功能不是必需的,但我只是以防万一...该函数接受以逗号分隔的函数名列表。每次调用函数都会在堆栈中创建一个新键,并表示传递的函数必须是串行执行的。但是,堆栈中的密钥可以按任何顺序执行。
eventExecAsyncFunction - 是一个由复选框valuechange事件触发的回调(感谢@Serge的概念!),这是允许列表成员异步执行的内容
cacheQuickEdit - 我所拥有的库函数的“国内”版本,它调用其他修改userCache的库函数。您可以更改这些调用以进行测试;先例函数大约是3行&学术。
// INTRODUCE A LIST OF FUNCTIONS TO BE EXECUTED ASYNCRONOUSLY
function asyncFunctionLaunch(Functions, app) {
var lockRef = LockService.getUserLock();
var bLock = lockRef.tryLock(10000);
app = app || UiApp.getActiveApplication();
Functions = Functions.split(',');
var vExecList = cacheQuickEdit(CACHEKEY.uiExecAsync);
var iOffset = 1;
var batchKey = String(new Date().getTime()) + '001';
batchKey = parseFloat(batchKey);
var vThisKey = {};
if (typeof vExecList != 'undefined') {
while (vExecList[batchKey]) {
iOffset++;
var sPadChar = stringPadLR(iOffset, '0', 3); // YOU DON'T NEED ALL THIS, A SIMPLE getTime() WILL SUFFICE AS THE KEY INDEX FOR TESTING
var batchKey = String(new Date().getTime()) + sPadChar;
batchKey = parseFloat(batchKey);
};
for (var iFunc in Functions) {
if (typeof vThisKey[Functions[iFunc]] == 'undefined') {
vThisKey[Functions[iFunc]] = {
start: false,
};
};
};
} else {
vExecList = {};
for (var iFunc in Functions) {
vThisKey[Functions[iFunc]] = {
start: false,
};
};
};
vExecList[batchKey] = vThisKey;
cacheQuickEdit(CACHEKEY.uiExecAsync, vExecList);
var oControl = app.getElementById('chkPreloadTrigger');
oControl.setValue(false, false);
oControl.setValue(true, true);
lockRef.releaseLock();
return app;
};
// RECURSIVELY EXECUTE CACHED LIST OF FUNCTIONS ASYNCRONOUSLY
function eventExecAsyncFunction(e) {
var lockRef = LockService.getUserLock();
var bLock = lockRef.tryLock(50000);
var sSource = e.parameter.source;
var vTriggerVal = (e.parameter[sSource] == 'true');
//var vTriggerVal = false; // FOR TESTING
var vExecList = cacheQuickEdit(CACHEKEY.uiExecAsync);
var app = UiApp.getActiveApplication();
var oChkPreload = app.getElementById('chkPreloadTrigger');
if (Object.keys(vExecList).length == 0) {
oChkPreload.setValue(false, false);
return app;
};
var sExec = '';
for (var sKey in vExecList) {
var vExec = vExecList[sKey];
// TEST FOR KEYS WITH FUNCTIONS BEING EXECUTED
var bExec = Object.keys(vExec).some(function(el) {
return (vExec[el].start == true);
});
if (bExec == false) {
// RETURN FIRST FUNCTION FROM KEY HAVING NO EXECUTING MEMBERS
sExec = Object.keys(vExec)[0];
break;
};
};
// NO NON-EXECUTING FUNCTIONS FOUND
if (sExec == '') {
// WORK FROM START OF KEY LIST
loop_exec_list: for (var sKey in vExecList) {
var vExec = vExecList[sKey];
for (var iExec in vExec) {
if (vExec[iExec].start == true) {
delete vExecList[sKey][iExec];
if (Object.keys(vExec).length < 1) {
delete vExecList[sKey];
};
} else {
sExec = Object.keys(vExec)[0];
break loop_exec_list;
};
};
};
};
var iKeys = Object.keys(vExecList).length;
if (sExec == '') {
oChkPreload.setValue(false, false);
lockRef.releaseLock();
return app;
};
vExecList[sKey][sExec].start = true;
var vNewExec = JSON.stringify(vExecList[sKey]);
var iFuncs = Object.keys(vExecList[sKey]).length - 1;
delete vExecList[sKey];
var iOffset = 1;
var batchKey = String(new Date().getTime()) + '001';
batchKey = parseFloat(batchKey);
while (vExecList[batchKey]) {
iOffset++;
var sPadChar = stringPadLR(iOffset, '0', 3);
var batchKey = String(new Date().getTime()) + sPadChar;
batchKey = parseFloat(batchKey);
};
vExecList[batchKey] = JSON.parse(vNewExec);
cacheQuickEdit(CACHEKEY.uiExecAsync, vExecList);
if (iFuncs > 0) { // IF THIS KEY CONTAINS MORE FUNCTIONS, TRIGGER CALLBACK AGAIN
oChkPreload.setValue(false, false);
oChkPreload.setValue(true, true);
};
lockRef.releaseLock();
app = this[sExec](app, e);
return app;
};
function cacheQuickEdit(sTargetCache, vValue) {
var lockRef = LockService.getUserLock();
var bLock = lockRef.tryLock(10000);
switch (true) {
case (typeof vValue == 'undefined'):
var vCache = nnGenericFuncLib.cacheLoadObject(sTargetCache);
if (vCache != null) {
var vObj = JSON.parse(vCache);
} else {
var vObj = {};
};
lockRef.releaseLock();
return vObj;
break;
case (vValue == ''):
nnGenericFuncLib.cacheDeleteObject(sTargetCache);
lockRef.releaseLock();
return null;
break;
default:
nnGenericFuncLib.cacheSaveObject(sTargetCache, JSON.stringify(vValue), nnGenericFuncLib.CACHE_TIMEOUT);
lockRef.releaseLock();
return sTargetCache;
break;
};
lockRef.releaseLock();
return false;
};
我的理论是回调允许堆栈中的函数并发执行,因为锁在调用之前终止,这意味着可以在第一次返回之前加载另一个函数来执行。我假设每个回调都发生在一个唯一的实例中,并且回调本身内没有冲突。
该计划的另一个关键部分是使用缓存,允许有序的堆栈执行。计划是将堆栈中第一个键的第一个函数标记为“已启动”,然后将该键移动到堆栈的末尾,然后再调用新的回调实例并最终执行该函数。
下一次调用再次找到第一个不包含已启动函数的键,并复制上一个进程。
当所有键都包含已标记为已启动的功能时,代码将再次选择列表中的第一个键,删除标记为已启动的功能(希望已完成),然后按照第一个过程 - 依此类推,直到所有键中的所有功能都被标记,执行和删除。
我在每个函数中都使用了锁,以防止缓存被提前覆盖或延迟。
所有这些的基础工作,但我通过(单独)在每次执行回调时缓存堆栈看到的是,年表不一致;这一切都有点'量子',我最终得到了太多的电话,而且没有足够的执行。
欢迎任何想法/建议!
- 编辑 - call-stack&amp; amp;屏幕截图完成后的顺序
答案 0 :(得分:0)
好吧花了几天时间,多愁眉流咬牙,还有一些脱发,但最后我发现了什么问题。 基本上,LockService在这种情况下完全无用(当你知道如何时很容易),所以我必须编写自己的&#34;并发锁服务&#34;。再加上一天的哀嚎,我终于得到了可靠的结果。
我不会发布我在这里提出的代码,因为在没有上下文的情况下很难准确地表示它,而且,现在我已经知道如何进一步改进它。但是,一旦我确实将它全部分类(并且已经进行了几天的按摩和头发替换疗法),我将在其上写一篇博客。让我知道你对此感兴趣,我稍后会发布更新:)