Javascript speed limit

How to limit the function to only 10 times per second, but continue execution when new spots are available? This means that we will call the function 10 times as soon as possible, and when 1 second has passed since the call of any function, we can make another call.

This description may be misleading, but the answer will be the fastest way to complete the X API call numbers given the speed limit.

Example: Here is an example that iterates over the alphabet to print each letter. How can we limit this to only printLetter 10 times per second? I still want to sort through all the letters, only with the appropriate speed.

 function printLetter(letter){ console.log(letter); } var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "Z"]; // How can I limit this to only run 10 times per second, still loop through every letter, and complete as fast as possible (ie not add a hard spacing of 100ms)? alphabet.forEach(function(letter){ printLetter(letter); }); 

Edit: Please do not answer in downvote format without leaving comments, explaining why this is a bad answer.

Edit 2: A good solution would not block every call for 100 ms. This makes the minimum run time of 1 second for 10 calls - when you could actually do this (almost) at the same time and potentially complete in a split second.

+6
source share
6 answers

Most of the other proposed solutions here evenly distribute function calls using an interval or recursive function with a timeout.

This interpretation of your question does not really do what I think you are asking because it requires you to call a function at a given interval.

If you want to limit how many times a function can be called regardless of the space between function calls, you can use the following method.


Define a factory function to hold the current time, quantity and queue, then return a function that checks the current time for the last recorded current time, and the count then executes the first element in the queue or waits for the next second to try again.

Pass the callback function to the function created by the factory function. The callback function will be entered into the queue. The limit function executes the first 10 functions in the queue, and then waits until this interval completes the next 10 functions until the queue is empty.

Return the limit function from the factory function.

 var factory = function(){ var time = 0, count = 0, difference = 0, queue = []; return function limit(func){ if(func) queue.push(func); difference = 1000 - (window.performance.now() - time); if(difference <= 0) { time = window.performance.now(); count = 0; } if(++count <= 10) (queue.shift())(); else setTimeout(limit, difference); }; }; var limited = factory(); var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); // This is to show a separator when waiting. var prevDate = window.performance.now(), difference; // This ends up as 2600 function calls, // all executed in the order in which they were queued. for(var i = 0; i < 100; ++i) { alphabet.forEach(function(letter) { limited(function(){ /** This is to show a separator when waiting. **/ difference = window.performance.now() - prevDate; prevDate = window.performance.now(); if(difference > 100) console.log('wait'); /***********************************************/ console.log(letter); }); }); } 
+9
source

You should do it a little differently:

 var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "Z"]; function printLetter(letterId) { if (letterId < alphabet.length) { // avoid index out of bounds console.log(alphabet[letterId]); var nextId = letterId + 1 if (nextId < alphabet.length) // if there is a next letter print it in 10 seconds setTimeout("printLetter(" + nextId + ")", 10000/*milliseconds*/); } } printLetter(0); // start at the first letter 

Demo:

 var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "Z"]; function printLetter(letterId) { if (letterId < alphabet.length) { // avoid index out of bounds console.log(alphabet[letterId]); document.body.innerHTML += "<br />" + alphabet[letterId]; // for ***DEMO*** only var nextId = letterId + 1 if (nextId < alphabet.length) // if there is a next letter print it in 10 seconds setTimeout("printLetter(" + nextId + ")", 100 /*milliseconds*/ ); // one second for ***DEMO*** only } } printLetter(0); // start at the first letter 
+1
source

The recursive version always looks cooler.

 // Print the first letter, wait, and do it again on a sub array until array == [] // All wrapped up in a self-invoking function var alphabet = ... var ms = 100 // 10 letters per seconds (function printSlowly( array, speed ){ if( array.length == 0 ) return; setTimeout(function(){ console.log( array[0] ); printSlowly( array.slice(1), speed ); }, speed ); })( alphabet, ms); 
+1
source

You can use setTimeout with a value of 100 (which is 1000 milliseconds / 10) to limit the output to 10 times per second. Use the call variable to count calls. If you want to call the same function in other places, remember to reset the call counter to 1, so you start a new one:

 var call = 1; function printLetter(letter){ call++; var x = call * 100; //alert(x); setTimeout(function(){ document.getElementById("test").innerHTML += letter; }, x); } var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "Z"]; // How can I limit this to only run 10 times per second, but still loop through every letter? alphabet.forEach(function(letter){ printLetter(letter); }); 
 <div id="test"/> 
0
source

Here's a recursive version with a callback (is that what you mean by "continue execution when new points are available"?)

EDIT: now it is even more abstracted - if you want to see the original implementation (very specific), see http://jsfiddle.net/52wq9vLf/0/

 var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "Z"]; /** * @function printLetter * @param {Array} array The array to iterate over * @param {Function} iterateFunc The function called on each item * @param {Number} start The index of the array to start on * @param {Number} speed The time (in milliseconds) between each iteration * @param {Function} done The callback function to be run on finishing */ function slowArrayIterate(array, iterateFunc, start, speed, done) { // Native array functions use these three params, so just maintaining the standard iterateFunc(array[start], start, array); if (typeof array[start + 1] !== 'undefined') { setTimeout(function() { slowArrayIterate(array, iterateFunc, start + 1, speed, done); }, speed); } else { done(); } }; slowArrayIterate(alphabet, function(arrayItem) { document.getElementById("letters").innerHTML += arrayItem; }, 0, 100, function() { // stuff to do when finished document.getElementById("letters").innerHTML += " - done!"; }); 

Here's jsfiddle: http://jsfiddle.net/52wq9vLf/2/

0
source

This is the best I could think of in the time I had.

Please note that this will not work correctly in Firefox v43 due to an error in the implementation of the fat-arrow functions.

 var MAX_RUNS_PER_WINDOW = 10; var RUN_WINDOW = 1000; function limit(fn) { var callQueue = [], invokeTimes = Object.create(circularQueue), waitId = null; function limited() { callQueue.push(() => { invokeTimes.unshift(performance.now()) fn.apply(this, arguments); }); if (mayProceed()) { return dequeue(); } if (waitId === null) { waitId = setTimeout(dequeue, timeToWait()); } } limited.cancel = function() { clearTimeout(waitId); }; return limited; function dequeue() { waitId = null ; clearTimeout(waitId); callQueue.shift()(); if (mayProceed()) { return dequeue(); } if (callQueue.length) { waitId = setTimeout(dequeue, timeToWait()); } } function mayProceed() { return callQueue.length && (timeForMaxRuns() >= RUN_WINDOW); } function timeToWait() { var ttw = RUN_WINDOW - timeForMaxRuns(); return ttw < 0 ? 0 : ttw; } function timeForMaxRuns() { return (performance.now() - (invokeTimes[MAX_RUNS_PER_WINDOW - 1] || 0)); } } var circularQueue = []; var originalUnshift = circularQueue.unshift; circularQueue.MAX_LENGTH = MAX_RUNS_PER_WINDOW; circularQueue.unshift = function(element) { if (this.length === this.MAX_LENGTH) { this.pop(); } return originalUnshift.call(this, element); } var printLetter = limit(function(letter) { document.write(letter); }); ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'X', 'Y', 'Z'].forEach(printLetter); 
0
source

All Articles