6

Would somebody mind to explain me what is the difference between these scripts below? The first one found here on Stack Overflow, the second one is my own version that I better understand but is not working. Why doesn't it work by the way?

1.

(function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
})();

2.

function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
  blink();
}
blink();
Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
divHelper11
  • 2,090
  • 3
  • 22
  • 37

6 Answers6

11

The first example is an Immediately-Invoked Function Expression (IIFE). It is a function which immediately executes itself after creation. IIFE is a common JavaScript design pattern used by most popular libraries (jQuery, Backbone.js, Modernizr, etc) to place all library code inside of a local scope.

In ES6 this can be rewritten using the Arrow function if you want the .blinkMe to blink just once:

(() => $('.blinkMe').fadeOut(500).fadeIn(500))();

You can chain as many fadeIn and fadeOut functions as you want of course. If you want it to loop infinitely I'd suggest the IIFE way.

More info about IIFE here.


About your second example. You're calling the function inside your own function (also known as recursive loop). In your case this creates an infinite loop, that's why it doesn't work. Remove the blink(); inside the function and it will work:

function blink() { 
    $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
}
blink();

Besides with javascript you can also solve this issue with CCS3 animations. CSS3 animations run in a separate thread. This is a solution without jQuery:

(function blink() {
  document.getElementsByClassName('blinkMe')[0].className += ' blinkActive';
})();

// Arrow function:
//(() => document.getElementsByClassName('blinkMe')[0].className += ' blinkActive')();
.blinkMe {
  width: 80px;
  height: 80px;
  background: deepskyblue;
}
.blinkActive {
  -webkit-animation: blink 1s infinite;
  animation: blink 1s infinite;
}
@-webkit-keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
<div class="blinkMe"></div>

I don't know how many elements you want to blink on your page, if it's just one element you can use ids instead of classes. Keep in mind that the @keyframes rule is not supported in IE9 or earlier versions and Opera Mini: Link.

Starfish
  • 3,344
  • 1
  • 19
  • 47
  • improve...I think imporant is this sentense "the code here is executed once in its own scope" and therefore this pattern is common used by jQuery plugins. – daremachine May 13 '16 at 23:33
  • @daremachine I've updated my answer with additional info. – Starfish May 13 '16 at 23:59
  • 1
    "In your case this creates an infinite loop, that's why it doesn't work" about the infinite loop isn't true. The correct versions are infinite loops (otherwise it would stop blinking). The incorrect version is an exponential loop, that is why it crashed. – Master DJon May 14 '16 at 03:38
  • 4
    Your first code snippet doesn't work -- blink is undefined. – Nathan Ringo May 14 '16 at 05:07
  • 2
    @MasterDJon no, the callback is never called because the JS thread is in an infinite loop that never yields. – OrangeDog May 14 '16 at 07:02
  • @tikiking1 Yes you are right. It was late for me, sorry. I didn't even notice it had to blink in a loop. I've edited my answer. – Starfish May 14 '16 at 11:30
  • @Patrick2607 I apologize. I was sure Timers were on a different thread, but I just check and they don't. This is really important to learn. – Master DJon May 14 '16 at 16:38
7

The first is a IIFE . That is, the function is immediately invoked (executed) after it is declared. It is not attached to the global window object and so does not pollute it.

The second function is exponentially recursive. Every time it is run, it calls itself twice more, once as a callback to the fadeIn jquery method, and once more in the function body.

Neil S
  • 2,304
  • 21
  • 28
6

The line $('.blinkMe').fadeOut(500).fadeIn(500, blink); already calls the blink when ending. So calling back blink() is like having twice the function called (and even more on each call). So this call was the problem.

EDIT : But as I just learned today, the timers aren't executed on a different thread in JavaScript. So, the blink() within the blink function takes all the executions frames and the blink callback is never called, but it is still registered to be called. As, I tested by adding a maximum number of iteration that the within blink() could be called.

function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
  //blink();
}
blink();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span class="blinkMe">Blinking</span>
Master DJon
  • 1,899
  • 2
  • 19
  • 30
6

In this snippet, a named function is created, then immediately executed once in an Immediately-Invoked Function Expression. The function name is only available to itself, so it can again be called within the closure of the function body, in this case as a callback to the fadeIn function.

(function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
})();


In this snippet, a named function is created, and called. Additionally, the function again calls itself, which will create an infinite recursion, which is why it fails. Unlike the last snippet, blink will also be available outside the function closure itself.

function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
  blink();
}
blink();

Removing the blink() call inside the function itself as follows will make it function nearly identical, except for the different in blink scope.

function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
}
blink();
Community
  • 1
  • 1
Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
2

This is a recursive function (animation).

According to the jQuery docs, fadeIn() takes two arguments: duration (in ms) and complete. The second argument is a function that is called once the animation is complete. It can be itself, or another function.

In the second function you are calling blink() again and again... but the second and the third never comming. Best way is the first.

Juanjo Salvador
  • 1,073
  • 1
  • 12
  • 33
2

Your function works, but the Navigator blocks it because of too much recursion. because you are callling blink() just after you fadeIn just try,

function blink() { 
  $('.blinkMe').fadeOut(500).fadeIn(500, blink); 
  //remove this call which causes too many recursions 
  //blink();
 }
//then call the method
 blink();
Nishanth Shaan
  • 1,422
  • 15
  • 18