Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > setTimeout and an object's methods

Reply
Thread Tools

setTimeout and an object's methods

 
 
VK
Guest
Posts: n/a
 
      03-06-2006

Thomas 'PointedEars' Lahn wrote:
> Utter nonsense, as usual.


Utter Thomas, as usual.

 
Reply With Quote
 
 
 
 
Richard Cornford
Guest
Posts: n/a
 
      03-13-2006
VK wrote:
> Andrew Poulos wrote:
>> With the following code I can't understand why
>> this.num keeps incrementing each time I create a
>> new instance of Foo. ...

<snip>
> I don't think you will be ever able to reach what you
> want in the way you want. window.setTimeout has been
> implemented waaay in JavaScript 1.0 then no one even
> thought about constructors, prototypes and stuff.
> He runs with this==window and no amount of braces around
> can change it. Any attempts to stick some other "this"
> are going away like water off a duck's back. So if I'm
> reading your intentions right:
>
> {
> {
> {
> ... and somewhere deep in the instance curlies
> setTimeout running for an instance method
> ...
>
> then NOOP - you cannot do it.
>
> There were some extensive discussion back in 90's (not
> here), and since nothing dramatically changed since then,


At least two things have changed over the history of - setTimeout -; the
number of implementations that allow a function reference to be passed
as the first argument to - setTimeout - has increased, to the point
where the facility's availability would be the norm, and it has been
observed that the language's preponderance for automatic type-conversion
provides a mechanism for supporting implementations where - setTimeout -
only recognises an initial string argument while only directly working
with function references arguments.

The practical result of which is that systems designed to employ the
manipulation of string arguments for - setTimeout - have tended to be
relegated to situations where strings have more significance than would
be normal in modern browser scripts, such as when using -
document.write - to insert HTML elements who's event handlers want to
call particular instance methods.

> you may stick to this solution.


Of available methods it would be the worst choice in most circumstances,
so you may well choose to stick to it as choosing the worst option of
any available in any circumstances does appear to be your habit.

> You can try to transform setTimeout default into its strength.
> If it's guranteed to run in the global scope, so give him a
> reference in a window property - you can be sure that it will
> pick it up.
>
> A sample can be found at
> <http://www.geocities.com/schools_ring/archives/threads.html>
> and the code posted at the end of this message - though I'm
> affraid newsreader may jam everything.


Most people are capable of learning to control their posting software
and ensuring that the code they post is acceptably presented.

> This sample is a kind of overkill, because I created it
> while testing script engine "thread capacity" plus some
> dynamic styling discrepancies between browsers. Nevertheless
> the idea is clear (I hope


It is certainly clear that the idea was not well understood by the
implementer of the example.

> 1) You give an OID (Object ID) to each instance.
> 2) On thread start you add new property to the current
> window where the key is instance OID and value is instance
> reference.


Any runtime stuffing of named properties into the global namespace
should be enough to flag a notion as misconceived, machine generated
Identifiers would be the worst candidates for that role as it would be
less than clear what the offending property names would be at the point
of insertion and so difficult to see what naming collisions would be
risked in the process.

> 3) On thread stop you remove this property.


That seems unnecessary as the inherent assumption that there will be no
naming collisions with other aspects of the system (while itself
reckless) implies that the identifier will not otherwise be used.

> 4) setTimeout is happy to use window[oid].method.


But not any more happy than it would be with -
MyObject.instances[instanceIndex].method(); - and risking no namespace
collisions.

> setTimeout is happy - you are happy


Being happy with a bad design where a much superior design is only a
stone's throw away is a characteristic of a poor script author, and
symptomatic of an inadequate thought process backup-up with superficial
testing.

<snip>
> function Timer(out) {
> // Instance class name:
> this.$name = 'Timer';
> // Instance OID (Object ID):
> this.$oid = this.$name + (new Date()).getTime();


It is known that the resolution of output from Date objects is rarely
better than 10 milliseconds. This means that if two objects are
instantiated within the 10 milliseconds over which the output of - (new
Date()).getTime() - does not change they will be given the same - $oid -
value, and then if they have activity scheduled with - setTimeout - that
overlaps at all the results will be chaotic.

That is chaotic behaviour, but only when three variable 'ifs' are
satisfied. This is the sort of scripting that follows from an inadequate
thought process and is not revealed in superficial testing.

The first variable 'if' is related to the number of objects
instantiated. The way in which the code is actually used in anger will
influence its susceptibility to the inadequacies in its design. Only
insatiate a single object and no problem will ever become apparent.

The second variable 'if' is time related. Code testing on a relatively
slow machine may guarantee that no two instances are created within the
critical interval, but that same code may reliably fail when exposed to
a significantly faster computer. A testing issue that becomes a
maintenance issue when you consider that computer hardware has
demonstrated a strong tendency to become faster over time. So while two
instances may never be created within the critical interval on current
computers the odds have got to be good that at some future point
hardware will become fast enough to expose the script's designed-in
susceptibility.

The third variable 'if' is again related to how the code is used. If two
instances that have been given identical IDs never attempt to schedule
activity in a way that overlaps no issue will be exposed.

So we have a design issue that has a predictable capacity for chaotic
outcomes that will vary in its manifestations depending on how the code
is actually used, the exact nature (mostly speed) of the hardware it
executes on, and vary with the precise timing of the commencement of
execution of the code. That anyone would be satisfied (or even "happy")
with this situation reveals a failure to understand the code as written
or a reckless disregard, where a non-joined-up thought process ends at a
superficial 'working' and no further consideration is given to the code
at all.

> Timer.$table[this.$oid] = this;


And yet just another couple of steps in the though process an you could
have created an issue-free implementation, and that line was the clue to
how it might be done.

The first step was not made because you are willing to disregard decades
of 'bast practice' advice (probably because it has too many 'academic'
associations, being explicitly taught in all formal programming
education) and effectively dynamically add global variables. Any genuine
programmer would have rejected that idea on principle and looked for a
way of uniquely referencing object instances that stayed out of the
global namespace.

A property of the object's constructor that was an Array or an object
would satisfy that requirement if used as a storage object for object
instances. You have created that object, and used it to store such
references, but failed to follow through to the point of realising that
it makes adding properties to the window object unnecessary, and so
avoids all issues that may result from that particular bad practice.
Instead of:-

var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';
$tid = window.setTimeout(cmd,1000);

-:-

var cmd = ' Timer.$table[\''+oid+'\'].$tick(\''+oid+'\')';
$tid = window.setTimeout(cmd,1000);

- would globally reference the particular object instance with the same
reliability as the former, but without even involving global namespace
issues, let alone risking manifestations of them.

Of course that is still subject to the issues arising from the poor
choice of - oid - value creation method. That issue follows from the
superficial though process surrounding the realisation of the need for
uniqueness in the IDs assigned to the objects. Timers provide a
uniqueness within a single machine (assuming that the machine's clock is
not perversely re-set) but with a limited resolution. That is an
adequate sort of uniqueness for tasks like discouraging machine-local
HTTP resource caching as the life of the cache is longer than the life
of the executing program, and the HTTP request/response process is
unlikely to take less time than the resolution of the timer (though
faster hardware _and_ internet connections could still make the timer
resolution an issue in the future).

The unique identification of an object instance in browser scripts does
not need to be nearly that unique as navigating away from the current
page is all that is necessary to invalidate any existing object
instances. A unique identifier for an object instance does not need to
be more unique than being unique to the specific instance of the
execution of the script. For which a simple sequence is sufficient,
i.e:-

var uniqueNum = (function(){
var num = 0;
return (function(){
return ++num;
});
})();

- and calls to - uniqueNum() - will return a sequence of
999999999999999934469 unique integers (and then break down). Quite
sufficient for use as script-execution unique identifiers (as browser
scripts would not be expected to run for long enough to get anywhere
near the point when that sequence ends).

However, IDs unique to the instance for the execution of the script are
also more than is necessarily (especially if they are not going to be
used in the global namespace). It is only necessary that the ID be
unique within the 'class' of the object instances. Hence the common
practice of using an array as the instance storage object and its -
length - property as ID of the object instance. E.G:-

function MyObject(){
MyObject.insts[(this.oid = MyObject.insts.length)] = this;
...
}

MyObject.insts = [];

MyObject.prototype.setTimer = function(){
setTimeout(('MyObject.insts['+this.oid+'].doSomething();'), 40);
};

MyObject.prototype.doSomething = function(){
...
};

And now all the issues are gone. No two object instances will have the
same ID during a single execution of the script, ever, and there is not
even a chance of a global namespace collision.

The design process doesn't have to be carried that much further to go
from the superficially 'working' to the reliable.

> // Output (DOM element):
> this.$out = out || null;
> // Timer ID:
> this.$tid = null;


Constant values assigned to each object instance are more effectively
created as properties of the constructor's prototype, as then all
instances are defaulted to the same value implicitly.

> // Minutes counter:
> this.$min = 0;


Ditto.

> // Seconds counter:
> this.$sec = 0;


Ditto.

> // Static methods (shared between instances):


"Static" methods are method 'of the class'. These are instance methods.
You can tell that they are instance methods because they use the -
this - keyword to refer to the object instance, and would not work if
executed in any way other than as a method of the object instance.

> this.start = Timer.$start;


Again, it is more efficient to create public instance methods as
properties of the constructor's prototype and have then inherited by
each object instance implicitly. It also makes for much clearer code as
the reader does not have to reconcile all (or worse, and in this case, a
sub-set of) the methods assigned to properties of the constructor as
object instance methods.

> this.$tick = Timer.$tick;


Ditto.

> this.stop = Timer.$stop;


Ditto.

> this.toString = Timer.toString;


Ditto.

> }
>
> Timer.$table = new Object();
>
> Timer.$start = function() {
> // Start timer instance:
> if (this.$tid == null) {
> var oid = this.$oid;
> var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';


Notice that the - $tick - method is to be executed as a method of the
object instance by the setTimeout call, and if it were not that method
would not execute as expected

> self[oid] = this;


As - Timer.$table[this.$oid] - will resolve as a reference to the object
instance form any context where 'Table' has not been locally declared
(and it hasn't anywhere here in practice, and would not be expected to
be in principle as the initial capital letter is usually used to
highlight a 'class' constructor) there is no reason to be adding a
reference to - this - as a property of the window object.

> this.$tid = window.setTimeout(cmd,1000);
> return this.$oid;
> }
> }
>
> Timer.$tick = function(oid) {
> // OnTimer instance call:
> with (self[oid]) {


As this function is executed as a method of an object instance (and must
be or it will behave in an implementation dependent way) there is no
need to refer to that object instance with - window[oid], and indeed no
need to pass the - oid - parameter at all. Using - with(this){ - would
be sufficient, if pointless as all the object properties can be
reference through the - this - keyword itself, and the result would be
muck clearer code.

> $sec++;
> if ($sec >= 60) {
> $min++;
> $sec = 0;
> }
> if ($out != null) {
> $out.innerHTML = this.toString($min,$sec);


You ca see here that the function must be executed as a method of the
object instance as otherwise the - this - keyword would refer to the
global object, which may or may not have a - toString - method, and if
it does it will not do what is expected with the arguments provided.

> }
> var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';
> $tid = window.setTimeout(cmd,1000);
> }
> }
>
> Timer.$stop = function() {
> // Stop timer instance:
> if (this.$tid != null) {
> window.clearTimeout(this.$tid);
> self[this.$oid] = null;


Having realised that there is no need for a global reference to the
object instance the effort of creating one, and subsequently nulling it
can be entirely avoided. There was never any good reason for nulling it
anyway as no code here acts conditionally based upon its existence.

> this.$tid = null;
> }
> }
>
> Timer.toString = function(m,s) {
> var t = '';
> if (arguments.length < 2) {
> t = 'function';
> }
> else {
> t = (m < 10)? '0'+m : m;
> t+= ':';
> t+= (s < 10)? '0'+s : s;
> }
> return t;
> }


Given that the last week has shown you suffering from misinterpreting
you own code where the omission of a statement-terminating semicolon
radically altered the meaning of the code in a way that was beyond your
conception, it would be a good idea for you to adopt the practice of
explicitly terminating statements with semicolons, including the
function expression assigning ExpressionStatements here. While you don't
know the rules for automatic semicolon insertion you cannot understand
when semicolons may be safely omitted.

> Timer.bDown = function(e) {


And this is where defining object instance methods as properties of the
constructor shows its folly, as these methods are 'of the class'. The
reader has to perceive that distinction for themselves instead of having
it made for them in the code as written. So the runtime overheads in not
employing the constructor's prototype is compounded by an ongoing
maintenance burden that would be completely avoidable.

> // Visual change of pseudo-button on mousedown:
> var evt = e || event;
> if (evt) {
> var trg = evt.target || evt.srcElement;
> trg.style.borderStyle = 'inset';
> }
> }

<snip>

These functions are all assigned to intrinsic event handling properties
of the created DOM elements. As such the - this - keyword will reliably
refer to the constructed element when they are executed, making the use
of - evt.target || evt.srcElement - to acquire a reference to that
element redundant, and potentially problem-causing as an event
intercepted during bubbling will not necessarily have the correct
element as its target or srcElement.

> Timer.action = function(e) {
> // Display current instance data and options:
> var evt = e || event;
> if (evt) {
> var trg = evt.target || evt.srcElement;
> var oid = trg.title;


So - trg.title - may not have the expected value.

> var tmp = Timer.$table[oid];


And so - Timer.$table[oid]; - may not return a valid instance reference.

> var msg = 'Instance OID: ' + oid +'\n\n';
> var val = tmp.toString(tmp.$min,tmp.$sec);


And if - tmp - is not a valid instance reference the code will likely
error-out here.

Learning the basics of javascript, which includes knowing how to
determine the predictable value of the - this - keyword depending on the
way in which a function is executed (instead of throwing up you hands in
horror and declaring - this - to be incontinent) combined with a more
joined-up thought process would significantly improve the code you
write. Which currently, as has been pointed out many times before,
usually represents the worst possible implementation of the worst
approach available. The code here is no exception.

Richard.


 
Reply With Quote
 
 
 
 
Dr John Stockton
Guest
Posts: n/a
 
      03-13-2006
JRS: In article <dv325i$18j$1$(E-Mail Removed)>, dated Mon, 13
Mar 2006 06:06:41 remote, seen in news:comp.lang.javascript, Richard
Cornford <(E-Mail Removed)> posted :


>It is known that the resolution of output from Date objects is rarely
>better than 10 milliseconds.


To clarify : the resolution of Date Objects is by specification one
millisecond. But new Date() gets a value from the OS, and it is
that which is often 10 ms or 50/60 ms.

<URL:http://www.merlyn.demon.co.uk/js-dates.htm#Ress> gives results for
various browsers. However, only the result for Win98/IE4 was measured
by me ...




>var uniqueNum = (function(){
> var num = 0;
> return (function(){
> return ++num;
> });
>})();
>
>- and calls to - uniqueNum() - will return a sequence of
>999999999999999934469 unique integers (and then break down).


9007199254740992 ??? (2^53) - Actually, with num++ you'd get one
more?

--
John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v4.00 IE 4
<URL:http://www.jibbering.com/faq/> JL/RC: FAQ of news:comp.lang.javascript
<URL:http://www.merlyn.demon.co.uk/js-index.htm> jscr maths, dates, sources.
<URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/jscr/&c, FAQ items, links.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Is there a way to find the class methods of a class, just like'methods' finds the instance methods? Kenneth McDonald Ruby 5 09-26-2008 03:09 PM
ASPX, VBScript, JavaScript, Double-Underscored Variables and setTimeOut Shadow Lynx ASP .Net 10 08-09-2006 07:40 PM
setTimeout in object methods using closures (old problem?) rain_c1@web.de Javascript 2 05-22-2006 03:25 PM
Smart navigation and js setTimeout Andy Pickering ASP .Net 1 10-27-2003 10:51 AM
Re: Templates and friends and template methods with private methods. Buster Copley C++ 5 07-07-2003 12:50 AM



Advertisments