Signal/Slot 机制
由于IoT领域中各种设备都是持续运作的,和脚本之间有事件响应式交互的需求,同时脚本编写过程中也会出现比较复杂的跳转需求,为了满足这些需求,我们参考Qt的语法引入了signal/slot机制。具体来说包括发起事件,以及事件响应。
要响应事件,需要定义事件响应函数,事件响应函数是在普通函数前面加入 [@slot](#)
的注解。
需要注意这类函数返回值会被忽略,也就是说如果一个函数是在响应事件,那么事件发起方式无法获得函数返回值的。同时由于脚本实际是单线程执行,所以显示发起的事件并不会立即执行,而是需要等待当前方法执行完成之后,对应的响应事件才会被执行。事件响应函数可以当作普通函数被脚本调用,这个时候和普通的函数没有区别。
发起事件语法是在类似方法调用的前面加上emit关键词:emit function(arg1, arg2, ...);
由于signal/slot机制和标准的ECMAScript语法存在比较大的差异,所以用户需要通过'use event'
标记来启用这个特性,如果没有启用这个特性而写了相关的代码,@slot
注解会被忽略,而emit
指令会在编译过程中报错。
具体如下面的例子:
'use event'
@slot
function slotFunc(a) {
print("slot " + a);
}
function test() {
for (let i = 0; i < 3; i++) {
print("test before emit " + i);
emit slotFunc("test " + i);
print("test after emit " + i);
}
}
print("main before emit");
emit slotFunc("external");
print("main after emit");
test();
上述脚本中定义了一个slot函数slotFunc(a)
,这个函数接受一个参数。后面的代码在主模块和函数内都可以发起事件,上述脚本的执行结果是:
“main before emit” “main after emit” “test before emit 0” “test after emit 0” “test before emit 1” “test after emit 1” “test before emit 2” “test after emit 2” “slot external” “slot test 0” “slot test 1” “slot test 2”
设备(劳动力)回调
设备(劳动力)在执行边缘下发的指令过程中也可以发起对脚本的回调,边缘平台的设备对接层通过调用平台Java API实现回调。需要注意的是,如果设备回调一个@slot事件响应函数,会导致脚本引擎中断当前流程,直接按照事件响应的方式执行该函数。如果设备回调的是一个普通函数,那么脚本在执行完函数之后,会继续等待原指令的返回。在原指令返回后,继续原流程的执行。比如有如下代码:
'use event'
@slot
function callback1() {
// 子流程逻辑
}
// 没有@slot注解
function callback2() {
// 子流程逻辑
}
let labor = laborService.getLabor();
// 设备可能产生回调 callback1() 或者 callback2()
labor.scan("page", "option", {"回调一":"callback1", "回调二":"callback2"});
labor.confirm("page");
在上述例子中,如果设备(劳动力)在scan这一步发起了callback1()
回调,那么后续的confirm()
将不会被执行,如果发起了callback2()
回调,那么在scan()
正常返回之后,后续的confirm()
会被执行。
尾调优化
在一个函数结尾处调用另一个函数,很容易出现递归调用的问题,最终会引发栈溢出。
为了避免gai
var idempotentKey = "idempotentKey-id-111";
var taskBatchId = "id-1232";
var sessionId = "session-id-123";
var result = "r-123";
var count = 0;
function scanLocation(){
return scanItem(result);
}
function scanItem(context){
return itemConfirm(context, "normal", false);
}
function itemConfirm(context, from, retry) {
if (count++ >= 100000) {
throw "completed";
}
return scanLocation(); //优化点
}
scanLocation();