setTimeout not working on Mac IE 5.2

Discussion in 'Javascript' started by Athanasius, May 3, 2005.

  1. Athanasius

    Athanasius Guest

    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>
     
    Athanasius, May 3, 2005
    #1
    1. Advertising

  2. Athanasius

    Fred Oz Guest

    Athanasius wrote:
    > 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>


    --
    Fred
     
    Fred Oz, May 5, 2005
    #2
    1. Advertising

  3. Athanasius wrote:
    > Could someone shed some light as to why the following
    > setTimeout function will not work on the Mac IE5.2?

    <snip>
    > 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.
     
    Richard Cornford, May 7, 2005
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Foreman
    Replies:
    5
    Views:
    233
    Foreman
    Jan 25, 2005
  2. tshad
    Replies:
    4
    Views:
    172
    tshad
    Dec 3, 2004
  3. James Black

    setTimeout not working on IE

    James Black, Apr 14, 2006, in forum: Javascript
    Replies:
    2
    Views:
    674
    Randy Webb
    Apr 14, 2006
  4. SetTimeout not working

    , Aug 14, 2006, in forum: Javascript
    Replies:
    3
    Views:
    164
    Dr John Stockton
    Aug 14, 2006
  5. Shan

    SetTimeout not working with IE

    Shan, Feb 22, 2008, in forum: Javascript
    Replies:
    4
    Views:
    133
    Thomas 'PointedEars' Lahn
    Feb 24, 2008
Loading...

Share This Page