Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

JavaScript and function.call and function.apply for async execution


:P
On this page:

A while back I posted about a JavaScript issue that I’ve run into quite commonly with callbacks and in working around DOM elements that don’t want to update properly until UI control returns to them. In those scenarios it’s essential to delay execution of a function call. You can then use window.setTimeout() to delay the execution easily enough;

 

Where this gets tricky is if you need to pass parameters and the question is how do you pass parameters to these calls. I’ve been using strings because that’s the only way I could figure out how to pass the parameters in the proper context. So for example to call a function that delays might look like this:

 

window.setTimeout("Fadeout('" + ControlId + "'," + HideOnZero.toString() + "," +

                  Step.toString() + "," + Percent.toString() + "," +

                   UseVisibility.toString() +")",30);

 

Where the various values passed as literals here are parameters of function. The idea here is that a Fadeout function calls itself with updated percentage parameters until it’s gone.

 

As several people pointed out in the original post it's not really a good idea to use strings for callbacks like this. It works but it gets messy real fast to write out those parameter strings, especially if you’re trying to do this in ASP.NET server code for embedding into the ClientScript object. The string delimiters get in the way real fast and it’s easy to make a typo in this sort of code.

 

So today an anonymous post replied on the original thread to look into function.apply and function.call. These functions essentially let you capture the current call context by passing an object reference that maintains the call context.

 

It’s easier to see in the resulting code:

 

window.setTimeout( function() {  Fadeout.call(this, ControlId,HideOnZero,

                                              Step,Percent, UseVisibility); },30);

setTimeout allows a function pointer, so this code creates a new function and uses Fadeout.call(this, ParmList). The this pointer in this case is a the current function and so this is the context that these parameter values use when the function is actually called.

 

It works and surely makes life a lot easier when using indirect calls to functions.

 

Function.apply is similar to call, except that it accepts an array of arguments as opposed to a fixed parameter list. This is actually easier as you can in most cases just pass the generic arguments array that JavaScript exposes:

 

window.setTimeout( function() {  Fadeout.call(this, arguments); },30);

or:

 

var Parms = new Array(ControlId,HideOnZero,Step,Percent,UseVisibility);

window.setTimeout( function() {  Fadeout.call(this, Parms); },30);

window.setTimeout( function() {  Fadeout.call(this, ["Hello",10] ); },30);

I’m not sure I really ‘understand’ how the context is kept in these calls and whether this results in some possible memory issues. But in some quick testing with several browsers it seems to work fine without any visible side-effects.


The Voices of Reason


 

Bertrand Le Roy
August 11, 2006

# re: JavaScript and function.call and function.apply for async execution

The context is kept because you're actually creating a closure.
Call or apply work nicely if you need to access the this pointer, but closures work just as well with any variable, which enables you to avoid the call or apply. that's of course what Function.createDelegate and Function.createCallback are doing in Atlas.
Example:
window.setTimeout(function() {Fadeout(somevariable, someothervariable);});
will retain only somevariable and someothervariable. The effect is as if you had tricked the setTimeout function to call your function with parameters that you specified in advance. That's one of the most useful tricks in JavaScript... :)

Rick Strahl
August 11, 2006

# re: JavaScript and function.call and function.apply for async execution

Thanks for the clarification Bertrand. It's new to me <g> and I agree: One of the useful tricks in JavaScript. I'm going throuhg some of my library code and cleaning up a lot of the message setTimeout() calls that were using strings with this.

Jason Haley
August 11, 2006

# Interesting Finds: August 10 part 2 and August 11, 2006


Jon Henning
August 16, 2006

# re: JavaScript and function.call and function.apply for async execution

Rick,
I just wanted to send a note saying how much I appreciate your blogs and how much help they have been on many issues. Reading this one brings me back to a point where I was at a while back in trying to maintain context in developing rich client-side controls. The idea of closures was really new to me at the time. This link provided me with the most help in understanding. Perhaps it will help you as well.
http://jibbering.com/faq/faq_notes/closures.html

In the off chance that your interested in how I utilize them in developing my open-source webcontrols see this document (http://dotnetnuke.com/LinkClick.aspx?fileticket=9FmZz0jvsi4%3d&tabid=874&mid=2653).

Thanks again for your willingness to share your experiences!

Rick Strahl
August 16, 2006

# re: JavaScript and function.call and function.apply for async execution

Hi Jon, thanks for the article link. This is an excellent article that explains a number of not so well understood JavaScript concepts in one place. Awesome!

Hallvord R. M. Steen
September 12, 2006

# re: JavaScript and function.call and function.apply for async execution

setTimeout actually takes an infinite number of arguments, the two first you know but the rest will be passed to the function. If you do not need the "this" object inside the Fadeout call I would say simply

window.setTimeout( Fadeout, 30, ControlId, HideOnZero, Step, Percent, UseVisibility);

Rick Strahl's Web Log
October 15, 2006

# JavaScript window.SetTimeout to a JavaScript Class method or function with parameters? - Rick Strahl's Web Log

Ah the joys of JavaScript and object orientation. Here's a little problem I can't seem to figure out on how to make call a class method from within a class (ie. no reference to a class instance) to another class method and pass parameters. Sounds simple, but I can't seem to find a way to do this and maintain encapsulation.

# DotNetSlackers: JavaScript and function.call and function.apply for async execution


Rick Strahl's Web Log
November 30, 2006

# Animated GIF images in hidden page elements - Rick Strahl's Web Log

I've run into a problem with animated gifs inside of a hidden area of a Web page that is hidden with style.display='none'. When the area is made visible again, in Internet Explorer this causes the image to not be displayed an animated GIF whatever I try.

Jason
November 30, 2006

# re: JavaScript and function.call and function.apply for async execution

This blog is filled with tons of great information! Thanks for having something like this available.

How would you go about calling a setTimeout for the "this.updateViewIE();" call in the following portion of code for an XSLTHelper class? My attempts have produced either "'updateViewIE' is undefined" or "'updateViewIE' is null or not an object" errors.

updateView: function () {
if ( ! XSLTHelper.isXSLTSupported() )
return;

if ( window.XMLHttpRequest && window.XSLTProcessor ) {
this.updateViewMozilla();
} else if ( window.ActiveXObject ) {
this.updateViewIE(); // *** Trying to setTimeout here ***
}
if(this.onload) this.onload.call(this);
},

updateViewMozilla: function() {
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(this.xslStyleSheet);
var fragment = xsltProcessor.transformToFragment(this.xmlDocument, document);

this.container.innerHTML = "";
this.container.appendChild(fragment);
},

updateViewIE: function(container) {
this.container.innerHTML = this.xmlDocument.transformNode(this.xslStyleSheet);
}

Thanks again for having this great tool for communication available!

Jason
November 30, 2006

# re: JavaScript and function.call and function.apply for async execution

This worked but it doesn't solve an animating gif issue that I've been having. It'll just delay the call for however long you set the timeout for so that the animated plays for that long. Then it stops:
var xsltHelp = new XSLTHelper( xmlURL, xsltURL, reloadDone );
window.setTimeout( function() { xsltHelp.loadView.call(xsltHelp, document.getElementById(state+"_outline")); }, 10);
Good luck to whoever tries this! (http://west-wind.com/weblog/ShowPost.aspx?id=1227)

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024