📜 ⬆️ ⬇️

JavaScript: limiting the frequency of execution

JavaScript is an amazing language with which sometimes you manage to do unexpectedly cool things. I want to introduce you to a slightly non-standard solution of a single speed problem that I recently encountered. Warning: not for beginners.

Baseline data: a resource-intensive function that updates certain elements on the screen when certain events occur (mouse movement, for example).
Problem: when events causing a function occur too often in a short period of time, the interface can begin to seriously slow down. Let's say if an event happens 1000 times in a few seconds, then the update is the same. For the interface, the lightning speed of rendering changes may not be so important, but the overall speed, which in this case suffers, is very much the case.
Task: to limit the function so that it is executed no more than once in a certain period of time. With a sufficiently small such interval, visually delays will not be noticeable, but the number of calls can be reduced several times, which in turn will very significantly reduce the load and help get rid of the inhibition.

We make some initial assumptions.
')
  1. Since the function must not always be executed, but selectively, a flag variable is necessary, in a special way setting and checking which choice can be made.
  2. If the function is called but not executable (the specified interval has not passed since the previous execution), you can’t just leave it - it must be executed at the end of the interval, otherwise the screen may not display the latest state after a series of events ( if the last call is not executed). For this you need a variable-flag, talking about such calls, which will be checked at the end of the interval.
  3. If there are several non-executable calls in a row, it is enough to postpone until the end of the interval, only the last one. To do this, we need to store in the variable the arguments of the last function call.
  4. Working in the manner described above, after the restriction, the function should retain the original scope and the arguments taken - obviously.


Let's move, in fact, to the code. We will sort it out later in order.

  var limitExecByInterval = function (fn, time) {	
	 var lock, execOnUnlock, args;
	 return function () {
		 args = arguments;
		 if (! lock) {				
			 lock = true;
			 var scope = this;
			 setTimeout (function () {
				 lock = false;
				 if (execOnUnlock) {
					 args.callee.apply (scope, args);
					 execOnUnlock = false;
				 }
			 }, time);
			 return fn.apply (this, args);
		 } else execOnUnlock = true;
	 }
 } 


limitExecByInterval takes the original function and the interval in milliseconds as input, returning the modified function that will be executed in the manner described above. (For simplicity, I did not do it with the Function.prototype method, although I can). Example of use:

var myFunc = function(...) { ... }
var myLimitedFunc = limitExecByInterval(myFunc, 150);


If desired, you can make it the method of any functions through Function.prototype . To do this, it is enough to change the first two lines as follows:

  Function.prototype.limitExecByInterval = function (time) {
	 var lock, execOnUnlock, args, fn = this;
	 ... 

Then you can call as follows:

var myLimitedFunc = myFunc.limitExecByInterval(150);

Now let's see what happens in the code.

First, we declare the variables we need - notice, outside the function being returned, - they will be common to all its calls. In the return function:
  1. save the call arguments (see clause 3 of the assumptions)
  2. if the lock box is not checked
    1. set the lock flag (p.1)
    2. save scope functions for the next step (p.4)
    3. we postpone for the given interval the following:
      • remove the lock flag (p.1)
      • if the execution flag is set at the end of the interval (p.2), remove it and perform the entire modified function.

    4. execute the original function with the given arguments, the same scope and return value. (p.4)


Here is the simplest example of working code. (An interesting moment: the difference with the use of restrictions and without distinctly noticeable in FF and IE, but in Opera and Safari everything is lightning fast in both versions).
I hope, clearly expressed. :) The thing is complicated, but personally it really helped me in practice to solve the real problem. Admit now in the comments, who understood what and what will be the comments. :)

Source: https://habr.com/ru/post/17884/


All Articles