From: Johannes Baagoe on
When making my test page to measure speeds of PRNGs, I met an
unexpected problem: neither Firefox nor Chrome displayed the changes
to the document before the end of the tests. (Firefox also became
*very* unresponsive.) Opera, on the other hand, behaved as expected -
the changes I made to the document were immediately visible.

I have solved the problem by daisy-chaining the tests with setTimeout
calls: I set up an Array of the elements I want to update (table
cells that contain the results of timing tests), and then I call a
recursive function that consumes them FIFO. Thus:

function time(elements) {
if (elements.length === 0) {
return;
}

var cell = elements.shift();
reset(cell);
window.setTimeout(time1, 0, cell);
window.setTimeout(time, 0, elements);
}

But I don't like it, it reminds me of gotos. Is there a better way ?

The page is here:
http://baagoe.com/en/RandomMusings/javascript/time.xhtml
and the relevant code here:
http://baagoe.com/en/RandomMusings/javascript/time.js

TIA.

--
Johannes
From: Evertjan. on
Johannes Baagoe wrote on 17 apr 2010 in comp.lang.javascript:

> window.setTimeout(time1, 0, cell);
> window.setTimeout(time, 0, elements);
> }

Not working the third parameter, in IE.

<script type='text/javascript'>
function testMe(x) {
alert(x);
return test;
};

var test = 1;
window.setTimeout(alert,1000,testMe(7));
test = 2;
alert(test);
</script>

The parameters are evaluated at setTimeout() setup time.

> But I don't like it, it reminds me of gotos. Is there a better way ?

Perhaps you do not know enough about GoTo, otherwise you would not say
this.

The main objection to GoTo, as voiced in 1968 by Edsger W. Dijkstra,
is it's non-modularity. <http://en.wikipedia.org/wiki/Edsger_Dijkstra>

setTimeout() on the other hand, is the superlative of modularity.

--
Evertjan.
The Netherlands.
(Please change the x'es to dots in my emailaddress)
From: Thomas 'PointedEars' Lahn on
Johannes Baagoe wrote:

> When making my test page to measure speeds of PRNGs, I met an
> unexpected problem: neither Firefox nor Chrome displayed the changes
> to the document before the end of the tests. (Firefox also became
> *very* unresponsive.) Opera, on the other hand, behaved as expected -
> the changes I made to the document were immediately visible.
>
> I have solved the problem by daisy-chaining the tests with setTimeout
> calls: I set up an Array of the elements I want to update (table
> cells that contain the results of timing tests), and then I call a
> recursive function that consumes them FIFO. Thus:
>
> function time(elements) {
> if (elements.length === 0) {
> return;
> }
>
> var cell = elements.shift();
> reset(cell);
> window.setTimeout(time1, 0, cell);
> window.setTimeout(time, 0, elements);

This is not very compatible, and potentially harmful. Use instead:

window.setTimeout(function() { time1(cell); }, 10);
window.setTimeout(function() { time(elements); }, 10);

More than two arguments for window.setTimeout() or window.setInterval(),
where the third, fourth aso. arguments are passed as arguments to the
function referred to by the first argument was introduced with Netscape
JavaScript 1.3 and is not necessarily available in all DOM implementations.
So you could end up calling the function with no arguments at all, if that.
This is definitely the case with the MSHTML DOM (which is why you might have
resorted to using XHTML that way, see below):

<http://msdn.microsoft.com/en-us/library/ms536753%28VS.85%29.aspx>

The 10 (ms) suggested here is the minimum delay value enforced in Mozilla-
based user agents for 32-bit platforms in order to avoid extensive CPU load
(on Windows), following a supposedly similar patch in MSHTML 6.0. Generally
you should not set the delay value below the length of the timer tick
interval of the runtime environment as the timeout delays add up otherwise
which causes said CPU load and can make your application look from sluggish
to unresponsive still (this is especially a problem with DOM animations).

<https://bugzilla.mozilla.org/show_bug.cgi?id=123273>
<news:1457265.ebmP4h5ntp(a)PointedEars.de>

> }
>
> But I don't like it, it reminds me of gotos. Is there a better way ?

Not one that is equally compatible and efficient. You might want to look
into Web Workers for newer Mozillas. But for compatible code you would need
to make more feature tests then, and you need to post messages back because
background threads cannot manipulate the DOM:

<https://developer.mozilla.org/En/Using_web_workers>

> The page is here:
> http://baagoe.com/en/RandomMusings/javascript/time.xhtml

I can see no good reason for using XHTML there. I can also see no good
reason for serving *only* non-"HTML-compatible" XHTML 1.0 Strict as
application/xhtml+xml. Why would you want to exclude e.g. the usually
relevant IE/MSHTML from the tests?

<http://www.w3.org/TR/xhtml-media-types/>

Anyhow, SHORTTAG syntax is not recommended for elements which content model
is not EMPTY, like `script', `th', and `td'. So use <th></th> instead of
<th/>.

<http://www.w3.org/TR/REC-xml/#NT-EmptyElemTag>

> and the relevant code here:
> http://baagoe.com/en/RandomMusings/javascript/time.js

init() does not make sense. You are only initializing an Object instance
there, and the variable you are initializing is declared global anyway. You
are not calling that function from anywhere else than the `onload' event-
handler attribute, so you can simply initialize the variable where you
declare it instead. (Keep in mind that variable instantiation, including
function declaration, happens before the first statement in an execution
context is evaluated; see ES5, 10.4.)

var generators = {
...
};

The identifier `RandomMathRandom' should be `randomMathRandom' as it does
not refer to a function that is called as a constructor. In fact, you can
lose the function declaration and initialize `generators.MathRandom' (which
should start lowercase as well) with a function expression without expected
loss of compatility or efficiency:

var generators = {
mathRandom: function () {
...
},

...
};

resetAll() and timeAll() --

function resetAll() {
var cells = document.getElementsByTagName('td');
for (var i = 0; i < cells.length; i++) {
reset(cells[i]);
}
}

function timeAll() {
var cells = [];
resetAll();
for (var i = 0; i < 3; i++) {
var rows = document.getElementsByTagName('tr');
for (var r = 0; r < rows.length; r++) {
if (rows[r].getElementsByTagName('td').length === 3) {
var cell = rows[r].getElementsByTagName('td')[i];
cells.push(cell);
}
}
}
time(cells);
}

-- can be a lot more efficient if you store the values of `cells.length',
`rows.length', and other repeatedly used, unchanging properties in a local
variable and use the variable value instead, and count backwards where
possible.

I would also not use strict comparison with host objects' properties; there
is no obvious speed advantage here (cf. ES5, 11.9), but you would lose the
advantage of implicit type conversion should an implementation not yield a
value of the expected type (here: Number). (The syntax compatibility loss
is considerably small, but present, too. See also
<http://PointedEars.de/es-matrix>.)

function resetAll()
{
var cells = document.getElementsByTagName('td');
for (var i = cells.length; i--;)
{
reset(cells[i]);
}
}

function timeAll()
{
var cells = [];
resetAll();
for (var i = 0; i < 3; i++)
{
var rows = document.getElementsByTagName('tr');
for (var r = 0, len = rows.length; r < len; ++r)
{
var row = rows[r];
if (row.getElementsByTagName('td').length == 3)
{
var cell = rows.getElementsByTagName('td')[i];
cells.push(cell);
}
}
}
time(cells);
}

However, you should also consider using the `rows', and the `cells' property
instead of getElementsByTagName(...); you would be increasing compatibility
that way, too. For example, if you know that the first cell is always a
`th' element, and the other cells in the row are always `td' elements, you
can count from 1:

function resetAll() {
var cells = document.getElementsByTagName('td');
for (var i = cells.length; i--)
{
reset(cells[i]);
}
}

function timeAll(table)
{
var cells = [];
resetAll();
for (var i = 1; i < 4; i++)
{
var rows = table.rows;
for (var r = 0, len = rows.length; r < len; ++r)
{
var rowCells = rows[r].cells;

if (rowCells.length == 4)
{
var cell = rowCells[i];
cells.push(cell);
}
}
}
time(cells);
}

Generally you would want to program more context-sensitive: You can pass a
reference to the table or row object to resetAll() so that you can make use
of its `rows' or `cells' properties, and reset only the cells of the table
or row that you are currently concerned with.


HTH

PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300dec7(a)news.demon.co.uk> (2004)
From: Johannes Baagoe on
Evertjan. :
> Johannes Baagoe :

>> window.setTimeout(time1, 0, cell);
>> window.setTimeout(time, 0, elements);

> Not working the third parameter, in IE.

Ahah, thanks for pointing it out.

> The parameters are evaluated at setTimeout() setup time.

Good to know.

>> But I don't like it, it reminds me of gotos. Is there a better way ?

> Perhaps you do not know enough about GoTo, otherwise you would not say
> this.

Perhaps I do not know enough about setTimeout :) And scripting for the
WWW in general.

Anyway, here, the stupid problem I had was that when I modified the
cells, nothing happened on the screen. What I wanted was a simple
way to say "Hey, browser, I mean *now*!". What I ended up with was
punching a ribbon in one procedure for another procedure to consume by
calling a non-standard deferred execution order sequentially, in order
implement a sort of "yield()" system call. In 2010, that is not the
sort of things I expect to have to do in a straightforward application.

Thanks a lot, anyway - the consensus seems to be that there is no
better way. Would it qualify as a FAQ?

--
Johannes
From: Johannes Baagoe on
Thomas 'PointedEars' Lahn :
> Johannes Baagoe :

>> window.setTimeout(time1, 0, cell);
>> window.setTimeout(time, 0, elements);

> This is not very compatible, and potentially harmful. Use instead:

> window.setTimeout(function() { time1(cell); }, 10);
> window.setTimeout(function() { time(elements); }, 10);

Aha! Of course. Nice. I feared I would have to use globals, or "local
globals" in a mammoth closure.

> Generally you should not set the delay value below the length of
> the timer tick interval of the runtime environment as the timeout
> delays add up otherwise which causes said CPU load and can make
> your application look from sluggish to unresponsive still (this is
> especially a problem with DOM animations).

My reasoning was "The implementor enforces a minimum, and she knows
more about what that minimum should be than I. I'll leave it to her".

But I shall follow your suggestion.

> <https://bugzilla.mozilla.org/show_bug.cgi?id=123273>
> <news:1457265.ebmP4h5ntp(a)PointedEars.de>

Great! Thanks.

>> But I don't like it, it reminds me of gotos. Is there a better way ?

> Not one that is equally compatible and efficient.

Ah, well. I was hoping for something like an event one could send to
the browser.

> <https://developer.mozilla.org/En/Using_web_workers>
>
>> The page is here:
>> http://baagoe.com/en/RandomMusings/javascript/time.xhtml
>
> I can see no good reason for using XHTML there. I can also see no good
> reason for serving *only* non-"HTML-compatible" XHTML 1.0 Strict as
> application/xhtml+xml. Why would you want to exclude e.g. the usually
> relevant IE/MSHTML from the tests?

No good reason, plenty of bad ones. I am more used to work with XML
than with SGML these days. I don't like when I can get away with
forgetting to close an element with a close tag, or quotes around
attribute values, etc. I have tools on my editor that presume XML. I
tend to use the same template to start all my web pages. I fairly
often embed SVG in XHTML. I use XSLT transforms. Etc...

I could pass all the XHTML pages trough a simple XSLT transform that
turns them into HTML 4.01 Strict. I could put the operation in a
Makefile, too... or in an Apache module. I know there is one.

More trouble than I have cared to take till now. Is it worth it?

> <http://www.w3.org/TR/xhtml-media-types/>

I shall have a closer look, but I fairly often put elements from
the SVG namespace into XHTML, and I may put in others...

> Anyhow, SHORTTAG syntax is not recommended for elements which content
> model is not EMPTY, like `script', `th', and `td'. So use <th></th>
> instead of <th/>.

> <http://www.w3.org/TR/REC-xml/#NT-EmptyElemTag>

Yeah, I know. But AFAICS, nobody is hurt. If I were a Kantian,
that would be no excuse, admittedly. I'm a Utilitarian, however,
so I need evidence that the integral of happiness over all sentient
beings increases if I write <th></th> instead of <th/>.

>> http://baagoe.com/en/RandomMusings/javascript/time.js

> init() does not make sense.

Quite. A leftover from another version.

> var generators = {
> ...
> };

> The identifier `RandomMathRandom' should be `randomMathRandom' as it
> does not refer to a function that is called as a constructor.

Well, it returns an Object of a particular kind. The upper case is
there to encourage users to think of it as returning that. No harm
that I can see in pretending it is a constructor and calling it with
new, if that is what one is used to do with Functions which return
an Object of a particular kind.

> In fact, you can lose the function declaration and initialize
> `generators.MathRandom' (which should start lowercase as well)
> with a function expression without expected loss of compatility
> or efficiency:

> var generators = {
> mathRandom: function () {
> ...
> },

ACK.

> resetAll() and timeAll() can be a lot more efficient if you store the
> values of `cells.length', `rows.length', and other repeatedly used,
> unchanging properties in a local variable and use the variable value
> instead, and count backwards where possible.

Sure, but they don't get called enough to make me hunt for optimisation.

> I would also not use strict comparison with host objects' properties;
> there is no obvious speed advantage here (cf. ES5, 11.9), but you would
> lose the advantage of implicit type conversion should an implementation
> not yield a value of the expected type (here: Number).

Isn't it *supposed* to yield a Number? Anyway, ACK.

> See also <http://PointedEars.de/es-matrix>.)

I shall.

> Generally you would want to program more context-sensitive: You can
> pass a reference to the table or row object to resetAll() so that
> you can make use of its `rows' or `cells' properties, and reset only
> the cells of the table or row that you are currently concerned with.

I shall, e.g., if I decide to allow users to click on the th elements
at the start of rows and columns in order to time all cases in one
row or column. Otherwise, I think I shall leave it as it is.


> HTH

It does indeed. Many thanks.

--
Johannes