mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	adding timeout attribute to function node
- [x] New feature (non-breaking change which adds functionality) Discussion here: https://discourse.nodered.org/t/function-node-doesnt-have-timeout-feature/78483 ## Proposed changes Adding a timeout attribute to the function node, so an endless funciton doesnt break the node red server. ## Checklist - [x] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md) - [x] For non-bugfix PRs, I have discussed this change on the forum/slack team. - [x] I have run `grunt` to verify the unit tests pass - [x] I have added suitable unit tests to cover the new/changed functionality
This commit is contained in:
		| @@ -82,6 +82,11 @@ | ||||
|                 <input id="node-input-outputs" style="width: 60px;" value="1"> | ||||
|             </div> | ||||
|  | ||||
|             <div class="form-row"> | ||||
|                 <label for="node-input-timeout"><i class="fa fa-clock"></i> <span data-i18n="function.label.timeout"></span></label> | ||||
|                 <input id="node-input-timeout" style="width: 60px;" value="1000"> | ||||
|             </div> | ||||
|  | ||||
|             <div class="form-row node-input-libs-row hide" style="margin-bottom: 0px;"> | ||||
|                 <label><i class="fa fa-cubes"></i> <span data-i18n="function.label.modules"></span></label> | ||||
|             </div> | ||||
| @@ -360,6 +365,7 @@ | ||||
|             name: {value:"_DEFAULT_"}, | ||||
|             func: {value:"\nreturn msg;"}, | ||||
|             outputs: {value:1}, | ||||
|             timeout:{value:0}, | ||||
|             noerr: {value:0,required:true, | ||||
|                     validate: function(v, opt) { | ||||
|                         if (!v) { | ||||
| @@ -464,6 +470,26 @@ | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             // 4294967295 is max in node.js timeout. | ||||
|             $( "#node-input-timeout" ).spinner({ | ||||
|                 min: 0, | ||||
|                 max: 4294967294, | ||||
|                 change: function(event, ui) { | ||||
|                     var value = this.value; | ||||
|                     if(value == ""){ | ||||
|                         value = 0; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         value = parseInt(value); | ||||
|                     } | ||||
|                     value = isNaN(value) ? 1 : value; | ||||
|                     value = Math.max(value, parseInt($(this).attr("aria-valuemin"))); | ||||
|                     value = Math.min(value, parseInt($(this).attr("aria-valuemax"))); | ||||
|                     if (value !== this.value) { $(this).spinner("value", value); } | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs, offset) { | ||||
|                 var editor = RED.editor.createEditor({ | ||||
|                     id: id, | ||||
| @@ -503,7 +529,7 @@ | ||||
|                 editor:this.editor, // the field name the main text body goes to | ||||
|                 mode:"ace/mode/nrjavascript", | ||||
|                 fields:[ | ||||
|                     'name', 'outputs', | ||||
|                     'name', 'outputs', 'timeout', | ||||
|                     { | ||||
|                         name: 'initialize', | ||||
|                         get: function() { | ||||
|   | ||||
| @@ -96,6 +96,13 @@ module.exports = function(RED) { | ||||
|         node.name = n.name; | ||||
|         node.func = n.func; | ||||
|         node.outputs = n.outputs; | ||||
|         node.timeout = n.timeout*1; | ||||
|         if(node.timeout>0){ | ||||
|             node.timeoutOptions = { | ||||
|                 timeout:node.timeout, | ||||
|                 breakOnSigint:true | ||||
|             } | ||||
|         } | ||||
|         node.ini = n.initialize ? n.initialize.trim() : ""; | ||||
|         node.fin = n.finalize ? n.finalize.trim() : ""; | ||||
|         node.libs = n.libs || []; | ||||
| @@ -362,6 +369,10 @@ module.exports = function(RED) { | ||||
|                     })(__initSend__);`; | ||||
|                     iniOpt = createVMOpt(node, " setup"); | ||||
|                     iniScript = new vm.Script(iniText, iniOpt); | ||||
|                     if(node.timeout>0){ | ||||
|                         iniOpt.timeout = node.timeout; | ||||
|                         iniOpt.breakOnSigint = true; | ||||
|                     } | ||||
|                 } | ||||
|                 node.script = vm.createScript(functionText, createVMOpt(node, "")); | ||||
|                 if (node.fin && (node.fin !== "")) { | ||||
| @@ -385,6 +396,10 @@ module.exports = function(RED) { | ||||
|                     })();`; | ||||
|                     finOpt = createVMOpt(node, " cleanup"); | ||||
|                     finScript = new vm.Script(finText, finOpt); | ||||
|                     if(node.timeout>0){ | ||||
|                         finOpt.timeout = node.timeout; | ||||
|                         finOpt.breakOnSigint = true; | ||||
|                     } | ||||
|                 } | ||||
|                 var promise = Promise.resolve(); | ||||
|                 if (iniScript) { | ||||
| @@ -396,9 +411,12 @@ module.exports = function(RED) { | ||||
|                     var start = process.hrtime(); | ||||
|                     context.msg = msg; | ||||
|                     context.__send__ = send; | ||||
|                     context.__done__ = done; | ||||
|  | ||||
|                     node.script.runInContext(context); | ||||
|                     context.__done__ = done;     | ||||
|                     var opts = {}; | ||||
|                     if (node.timeout>0){ | ||||
|                         opts = node.timeoutOptions; | ||||
|                     } | ||||
|                     node.script.runInContext(context,opts); | ||||
|                     context.results.then(function(results) { | ||||
|                         sendResults(node,send,msg._msgid,results,false); | ||||
|                         if (handleNodeDoneCall) { | ||||
|   | ||||
| @@ -214,7 +214,8 @@ | ||||
|             "initialize": "Start", | ||||
|             "finalize": "Stopp", | ||||
|             "outputs": "Ausgänge", | ||||
|             "modules": "Module" | ||||
|             "modules": "Module", | ||||
|             "timeout": "Timeout (ms)" | ||||
|         }, | ||||
|         "text": { | ||||
|             "initialize": "// Der Code hier wird ausgeführt,\n// wenn der Node gestartet wird\n", | ||||
|   | ||||
| @@ -248,7 +248,8 @@ | ||||
|             "initialize": "On Start", | ||||
|             "finalize": "On Stop", | ||||
|             "outputs": "Outputs", | ||||
|             "modules": "Modules" | ||||
|             "modules": "Modules", | ||||
|             "timeout": "Timeout (ms)" | ||||
|         }, | ||||
|         "text": { | ||||
|             "initialize": "// Code added here will be run once\n// whenever the node is started.\n", | ||||
|   | ||||
| @@ -212,7 +212,8 @@ | ||||
|             "function": "Функция", | ||||
|             "initialize": "Настройка", | ||||
|             "finalize": "Закрытие", | ||||
|             "outputs": "Выходы" | ||||
|             "outputs": "Выходы", | ||||
|             "timeout":"Время ожидания (мс)" | ||||
|         }, | ||||
|         "text": { | ||||
|             "initialize": "// Добавленный здесь код будет исполняться\n// однократно при развертывании узла.\n", | ||||
|   | ||||
| @@ -1424,7 +1424,30 @@ describe('function node', function() { | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     it('should timeout if timeout is set', function(done) { | ||||
|         var flow = [{id:"n1",type:"function",wires:[["n2"]],timeout:"10",func:"while(1==1){};\nreturn msg;"}]; | ||||
|         helper.load(functionNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             n1.receive({payload:"foo",topic: "bar"}); | ||||
|             setTimeout(function() { | ||||
|                 try { | ||||
|                     helper.log().called.should.be.true(); | ||||
|                     var logEvents = helper.log().args.filter(function(evt) { | ||||
|                         return evt[0].type == "function"; | ||||
|                     }); | ||||
|                     logEvents.should.have.length(1); | ||||
|                     var msg = logEvents[0][0]; | ||||
|                     msg.should.have.property('level', helper.log().ERROR); | ||||
|                     msg.should.have.property('id', 'n1'); | ||||
|                     msg.should.have.property('type', 'function'); | ||||
|                     should.equal(msg.msg.message, 'Script execution timed out after 10ms'); | ||||
|                     done(); | ||||
|                 } catch(err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             },50); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe("finalize function", function() { | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user