diff --git a/examples/tests_module.js b/examples/tests_module.js index 4aefecc..9bc4fd2 100644 --- a/examples/tests_module.js +++ b/examples/tests_module.js @@ -94,3 +94,8 @@ exports.multipleCallbacks = (...cb)=>{ } return 'bar'; }; + +exports.multiCallsCallback = (cb,times)=>{ + for(var i=0; ithis.proxyPromiseResult(proxyPromise, actionData, ...args)); this.proxyCom.transport.send("proxyAction", actionData); } - proxyPromiseArguments(proxyPromise,actionData){ + handlePromiseArguments(proxyPromise,actionData){ + var { proxyOptions } = proxyPromise; var { interfaceID, actionID, args, argsConfig } = actionData; + var callbackLimit = _.has(proxyOptions,'callbackLimit') ? proxyOptions.callbackLimit*1 : 1; + var callbackTimeout = _.has(proxyOptions,'callbackTimeout') ? proxyOptions.callbackTimeout*1 : 0; + var callbackStopPromise = _.has(proxyOptions,'callbackStopPromise') && _.isPromise(proxyOptions.callbackStopPromise) && proxyOptions.callbackStopPromise; + var callbackOnRemove = _.has(proxyOptions,'callbackOnRemove') && _.isFunction(proxyOptions.callbackOnRemove) && (()=>{ + try{ proxyOptions.callbackOnRemove(); }catch(err){}; + }); args.forEach((arg,i)=>{ if(_.isFunction(arg)){ var argumentActionID = proxyPromise.argsActionID++; var returnEventName = 'proxyActionArgument:' + interfaceID + ':' + actionID + ':' + argumentActionID; args[i] = null; argsConfig[i] = { type:'function', returnEventName }; - this.proxyCom.transport.once(returnEventName, (funcArgs)=>arg(...funcArgs)); + var onListener = null, onceListener = null; + var callArg = (funcArgs)=>{ + try{ arg(...funcArgs); }catch(err){} + }; + var cancelListener = ()=>{ + cancelListener = ()=>{}; + proxyPromise.removeArgumentCallbackListeners(); + }; + this.proxyCom.transport.once(returnEventName+':cancel',cancelListener); + var removeCancelListener = ()=>{ + removeCancelListener = ()=>{}; + this.proxyCom.transport.removeListener(returnEventName+':cancel',cancelListener); + }; + if(callbackLimit>1) { + var callCount = 0; + onListener = (...funcArgs)=>{ + if(callCount>callbackLimit) return; + callCount++; + callArg(...funcArgs); + if(callCount>=callbackLimit) proxyPromise.removeArgumentCallbackListeners(); + }; + } + else if(callbackLimit===0) onListener = callArg; + else onceListener = (...funcArgs)=>{ + callArg(...funcArgs); + removeCancelListener(); + if(callbackOnRemove) callbackOnRemove(); + }; + if(onListener!==null) this.proxyCom.transport.on(returnEventName,onListener); + else if(onceListener!==null) this.proxyCom.transport.once(returnEventName,onceListener); + var tmr = null; + proxyPromise.removeArgumentCallbackListeners = ()=>{ + proxyPromise.removeArgumentCallbackListeners = ()=>{}; + if(tmr){ clearTimeout(tmr); tmr = null; } + if(onListener!==null) this.proxyCom.transport.removeListener(returnEventName,onListener); + else if(onceListener!==null) this.proxyCom.transport.removeListener(returnEventName,onceListener); + if(callbackOnRemove) callbackOnRemove(); + removeCancelListener(); + }; + if(callbackTimeout>0){ + tmr = setTimeout(()=>proxyPromise.removeArgumentCallbackListeners(),callbackTimeout); + } + if(callbackStopPromise){ + callbackStopPromise.then(()=>proxyPromise.removeArgumentCallbackListeners()); + } } }); } - proxyPromiseResult(promiseObj, actionData, resultObj) { - var {resolve: resolveOriginal, reject: rejectOriginal} = promiseObj; + proxyPromiseResult(proxyPromise, actionData, resultObj) { + var {resolve: resolveOriginal, reject: rejectOriginal} = proxyPromise; var { error, type, result } = resultObj; if (actionData.handledResult) return; actionData.handledResult = true; var resolve = (obj)=>this.proxyPromiseResult_parseObject(actionData, obj, resolveOriginal); - var reject = (obj)=>this.proxyPromiseResult_parseObject(actionData, obj, rejectOriginal); + var reject = (obj)=>{ + if('removeArgumentCallbackListeners' in proxyPromise) proxyPromise.removeArgumentCallbackListeners(); + return this.proxyPromiseResult_parseObject(actionData, obj, rejectOriginal); + }; if (error) { var errorResult = this.proxyPromiseResult_error(error); if (actionData.actionConfig && actionData.actionConfig.resolveError) return resolve({error: errorResult}); diff --git a/lib/proxy-handler.js b/lib/proxy-handler.js index 96e1b6c..edfba90 100644 --- a/lib/proxy-handler.js +++ b/lib/proxy-handler.js @@ -271,6 +271,8 @@ const dataHandler = exports.dataHandler = class dataHandler { var { actionData, target, result, targetMethod } = handleData; var { returnEventName, interfaceID, actionID, property, newOperator, args, argsConfig, actionConfig } = actionData; var resultTarget = hasResult ? result : target[property]; + // Handle arguments + this.handleActionArguments(handleData); // Target Methods switch (targetMethod) { // Promisify Function @@ -292,18 +294,11 @@ const dataHandler = exports.dataHandler = class dataHandler { // Function case targetMethods.function: if (!hasResult){ - // Handle arguments - _.each(argsConfig,(config,i)=>{ - if(config.type==='function'){ - args[i] = (...funcArgs)=>{ - this.proxyCom.transport.send(config.returnEventName, funcArgs); - } - } - }); // Call function let r; if (newOperator) r = new target[property](...args); else r = target[property](...args); + handleData.actionData.args = []; handleData.actionData.argsConfig = {}; handleData.result = r; return this.handleActionTarget(handleData); } else { @@ -385,4 +380,22 @@ const dataHandler = exports.dataHandler = class dataHandler { } } + handleActionArguments(handleData){ + var targetMethod = handleData.targetMethod; + var { args, argsConfig } = handleData.actionData; + _.each(argsConfig,(config,i)=>{ + if(config.type==='function'){ + if(targetMethod!==targetMethods.function){ + this.proxyCom.transport.send(config.returnEventName+':cancel'); + args[i] = ()=>{}; + } + else { + args[i] = (...funcArgs)=>{ + this.proxyCom.transport.send(config.returnEventName, funcArgs); + } + } + } + }); + } + }; diff --git a/tests/2-data-types.js b/tests/2-data-types.js index 3c8dde7..4c1a646 100644 --- a/tests/2-data-types.js +++ b/tests/2-data-types.js @@ -527,6 +527,57 @@ describe("Require-Worker Data Types",()=>{ }).catch(done); }); + it("Multiple Callback Calls via .configure({ callbackLimit:x })",function(done){ + this.timeout(500); + var done2 = _.after(10,done); + var checkCount = 0; + proxy.multiCallsCallback((i)=>{ + checkCount++; + done2(); + },8) // Anymore than 8 will simply be ignored on the client + .configure({ callbackLimit:8, callbackOnRemove:()=>{ + if(checkCount<8) done("Callback Limit Not Reached? checkCount="+checkCount); + else done2(); + } }) + .then(({value})=>{ + expect(value).to.equal(true); + done2(); + }).catch(done); + }); + + it("Multiple Callback Calls hit Limit",function(done){ + this.timeout(500); + var done2 = _.after(3,done); + var checkCount = 0; + proxy.multiCallsCallback((i)=>{ + checkCount++; + if(checkCount>1) done("Callback Calls Over Limit? checkCount="+checkCount); + else done2(); + },2) + .configure({ callbackLimit:1, callbackOnRemove:()=>{ + done2(); + } }) + .then(({value})=>{ + expect(value).to.equal(true); + done2(); + }).catch(done); + }); + + it("Multiple Callback Calls no Limit",function(done){ + this.timeout(500); + var done2 = _.after(2,done); + var checkCount = 0; + proxy.multiCallsCallback((i)=>{ + checkCount++; + if(checkCount>=10) done2(); + },10) + .configure({ callbackLimit:0 }) + .then(({value})=>{ + expect(value).to.equal(true); + done2(); + }).catch(done); + }); + }); });