setTimeout's Secret Free

Read the title how you wish, setTimeout has a secret it has been holding onto for window.Infinity milliseconds.

tl;dr: Solution 3.

The Problem

Because setTimeout is defined globally on the window object, the this binding frequently becomes an issue. Case in point:

// Goal: print 0, 1...8
for( var i = 0; i < 9; i++ ) {  
  setTimeout(function() {
     console.log( i );
  }, 0);
}

Output:

The number 9 printed 9 times

Solution 1

for( var i = 0; i < 9; i++ ) {  
  setTimeout(function() {
     console.log( this );
  }.bind( i ), 0);
  // bind context
}

Output:

primitive values of 0 through 8 are printed

In this example, rather than binding a real context, we bind the only thing that matters to us--the variable i.

Now, why the weird PrimitiveValue output? You must bind to an object, not a primitive; as a result we bind to new Number( i ).*

Solution 2

for( var i = 0; i < 9; i++ ) {  
  setTimeout(function( val ) {
     console.log( val );
  }.bind( null, i ), 0);
  // bind with args
}

Output:

 0 through 8 are printed

In this example, we don't care about the context thisArg at all. Whatever is passed into bind after the thisArg becomes a parameter (val), which is fed into the anonymous function.

Solution 3 (Personal Fave)

for( var i = 0; i < 9; i++ ) {  
  setTimeout(function( val ) {
     console.log( val );
  }, 0, i);
  // Huh? The bind is gone!
}

Output:

 0 through 8 are printed

A solution without bind and the namesake for this post. setTimeout doesn't just take a function and milliseconds as parameters, it also takes values which it will gladly pass to the function inside:

documentation for setTimeout from MDN

Further Reading:
  • A quick review of JavaScripts's bind function can be found here.
  • Read more about setTimeout at MDN.
  • Learn the difference between new Number( 3 ) and Number( 3 ).
  • More on JavaScript primitives.

*Thanks to Andrew Teich for eloquently explaining binding, or lack-thereof, to primitives.

comments powered by Disqus