setTimeout not working on Mac IE 5.2

A

Athanasius

Could someone shed some light as to why the following setTimeout
function will not work on the Mac IE5.2? It does however work on
PC(Forefox,Netscape,IE) & Mac(Safari,Firefox). Here is the script, it
should simply present an alert message after 5 seconds. Thanks.

<html>
<head>
<script language="javascript">
function MsgTimer() {
var self = this;
var mytimer;
var seccnt = 0;

// Private Method
var thetimer = function() {
if (seccnt >= self.secs) {
clearTimeout(mytimer);
alert("Waited: " + self.secs + " seconds to show you: " +
self.msg);
} else {
seccnt++;
mytimer = setTimeout(thetimer,1000);
}
}

// Public Method
self.showmsg = function(msg,secs) {
self.msg = msg;
self.secs = secs;
thetimer();
}

}
</script>
</head>

<body>

<h3>Simple Timer Function</h3>
<script language="javascript">
var mt = new MsgTimer();
mt.showmsg('Hello World',5);
</script>

</body>
</html>
 
F

Fred Oz

Athanasius said:
Could someone shed some light as to why the following setTimeout
function will not work on the Mac IE5.2? It does however work on
PC(Forefox,Netscape,IE) & Mac(Safari,Firefox). Here is the script, it
should simply present an alert message after 5 seconds. Thanks.

<html>
<head>
<script language="javascript">
function MsgTimer() {
var self = this;
var mytimer;
var seccnt = 0;

// Private Method
var thetimer = function() {
if (seccnt >= self.secs) {
clearTimeout(mytimer);
alert("Waited: " + self.secs + " seconds to show you: " +
self.msg);
} else {
seccnt++;
mytimer = setTimeout(thetimer,1000);
}
}

// Public Method
self.showmsg = function(msg,secs) {
self.msg = msg;
self.secs = secs;
thetimer();
}

}
</script>
</head>

<body>

<h3>Simple Timer Function</h3>
<script language="javascript">
var mt = new MsgTimer();
mt.showmsg('Hello World',5);
</script>

</body>
</html>

No idea, but maybe a browse here may help:

<URL:http://www.dhtmlcentral.com/forums/topic.asp?TOPIC_ID=17585&whichpage=2>
 
R

Richard Cornford

Athanasius said:
Could someone shed some light as to why the following
setTimeout function will not work on the Mac IE5.2?
var thetimer = function() {
if (seccnt >= self.secs) {
clearTimeout(mytimer);
alert("Waited: " + self.secs + " seconds to show you: " +
self.msg);
} else {
seccnt++;
mytimer = setTimeout(thetimer,1000);
<snip>

Early setTimeout implementations took a string as their first argument
and (when the interval timed-out (approximately)) evaluated that string.
More recent implementations added the ability to use a reference to a
function object as the first argument, such that the function was
executed at the end of the interval. Windows IE versions added support
for this second option with the release if IE 5.0. I don't recall the
situation with Mac IE but as your are using a function reference as the
first argument it is possible that this is not working because that Mac
IE version's setTimeout only supports string arguments.

There are two strategies for handling this particular incompatibility.
The first, and most obvious, is to only use string arguments with
setTimeout. That will not help you at all in this context as your are
using a reference to an inner function and the string arguments to
setTimeout are evaluated in the global context (where the inner function
is invisible). It would also be a pity to be abandoning the use of the
more efficient function references with setTimeout on more recent
browsers just for the sake of supporting older browsers (especially when
there is an alternative).

The second strategy appears to have been noticed (invented?) a couple of
years ago, and takes advantage of javascript's tendency to type-convert
values into the type required for a particular context. When setTimeout
only supports string arguments it type-converts whatever argument it is
given into a string, and then evaluates that string. Type-converting a
Function reference into a string would involve calling the function
object's - toString - method. By default that method is the one defined
by - Function.prototype.toString -, and returning an implementation
dependant value that is normally a normalised version of the function's
definition text. So it would be that text that was evaluated by the
setTimeout function. Evaluating that text will not result in an error,
but it does not result in the calling of the function (it actually
represents the evaluation of a function expression).

The trick is to replace the function's - toString - method with an
alternative that will result in the execution of the function. For
example:

function demo(){
// function body.
}
demo.toString = function(){
return 'demo();';
};
setTimeout(demo, 400);

- passes a function reference to - setTimeout - and so takes advantage
of the more efficient execution of function references available on more
recent browsers. On a browser that only supports string values as the
first argument to - setTimeout - the function reference is
type-converted into a string by calling its - toString - method, which
returns the string "demo();", and that is a function call. The function
is called regardless of the - setTimeout - implementation, and in the
most efficient way available.

However, this will not handle your inner function problem because the
string returned form the call to the function's - toStirng - method
would still be evaluated in the global context. That is not a problem in
the 'demo' example above because the - demo - function is defined in the
global context. This is not an insurmountable problem. The solution is
to have the - toString - method also expose the inner function in the
global context. One approach might be:-

function example(){
function exampleInner(){
// function body;
}
exampleInner.toString = globalFuncToString;
setTimeout(exampleInner, 400);
}

function globalFuncToString(){
globalFuncToString.execut = this; //this - is a reference to the inn
er function
return 'globalFuncToString.execut();';
}

- where a global function is assigned as the - to String - method of the
inner function. When that global function is called, during the
type-conversion process, it is a executed as a method of the inner
function so the - this - keyword is a reference to that inner function.
A reference to that inner function is assigned (using - this-) to a
public property of the global function and the returned string is code
that calls the inner function as a method of the global function (which
is not significant as the inner function cannot usefully employ the -
this - keyword itself).

The above would work, but only with one setTimeout at a time, which is
probably not satisfactory. The problem is that there is only one
globally accessible reference being used so if two or more setTimout's
overlapped they would tend to execute only the last function assigned
(and setTieouts are likely to overlap).

A more elaborate global function used as inner function's - toString -
methods might adopt a more flexible approach, creating a separate
globally accessible property for each function used. E.G:-

function example(){
function exampleInner(){
// function body;
}
exampleInner.toString = globalToString;
setTimeout(exampleInner, 400);
}

function globalToString(){
if(!globalToString.ex){
globalToString.ex = [];
}
var c = globalToString.ex.length;
var st = 'globalToString.ex['+c+']();';
globalToString.ex[c] = this;
return (this.toString = function(){
return st;
})();
}

In this case each function that uses this globalToString function
creates a new entry in an array, and then replaces the function's to
string method with a function that returns a string that calls the
function as an indexed element of that array. So many functions will
work with this version at the same time, and the same function
repeatedly without any additional work.

The drawback with this version is that all of those function references
remain assigned as elements of the array. If they are inner functions
then the resulting closures will be indefinitely preserved.

An alternative is to make the event scheduling the responsibility of a
separate external function/object and have that function do the work of
providing the fall-back for older setTimout implementations. The inner
function references would be passed to that function/object and it would
schedule their execution. I frequently use do this for dynamic animation
in scripts, which is similar to your original requirement to execute an
inner function at one second intervals, except that for animation the
intervals must be shorter. This is the current version of the animation
timer that I use:-

var TimedQue = (function(){
var base, timer;
var interval = 42;
var newFncs = null;
function addFnc(next, f){
function t(){
next = next&&next();
if(f()){
return t;
}else{
f = null;
return next;
}
};
t.addItem = function(d){
if(next){
next.addItem(d);
}else{
next = d;
}
return this;
};
return t;
};
function tmQue(fc){
if(newFncs){
newFncs = newFncs.addItem(addFnc(null, fc));
}else{
newFncs = addFnc(null, fc);
}
if(!timer){
timer = setTimeout(tmQue.act, interval);
}
};
tmQue.act = function(){
var fn = newFncs, strt = new Date().getTime();
if(fn){
newFncs = null;
if(base){
base.addItem(fn);
}else{
base = fn;
}
}
base = base&&base();
if(base||newFncs){
var t = interval - (new Date().getTime() - strt);
timer = setTimeout(tmQue.act, ((t > 0)?t:1));
}else{
timer = null;
};
};
tmQue.act.toString = function(){
return 'TimedQue.act()';
};
return tmQue;
})();

It is called as:-

TimedQue(functionReference);

- where - functionReference - is always a reference to a function. That
function will be executed at intervals defined by the - interval -
variable, and any number of functions can be added to the process (all
will be executed at the specified interval). The contract for those
functions passed by reference to the - TimedQue - function is that they
return true for as long as they want to continue being executed, and
they return false when they no longer want to be executed at the
specified interval. Thus the function references are freed when they
leave the scheduling process.

The - TimedQue - function handles the fall-back for string-argument-only
setTimeout implementations itself, so the referenced functions do not
have to implement it themselves.

It is because of the use of this strategy that I do not remember whether
Mac IE 5 supports function references with the setTimeout function. My
code would work regardless of the browser's implementation of
setTimeout, and still keeps the simplicity of using function references
(including inner functions) so I don't need to remember.

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top