Difference between revisions of "Useful functions"
From SoundDB
					
										
					
					| Line 74: | Line 74: | ||
|      return e1>e2 ? -1:(e1<e2 ? 1:0) |      return e1>e2 ? -1:(e1<e2 ? 1:0) | ||
| }) | }) | ||
| + | </pre> | ||
| + | |||
| + | <pre> | ||
| + | <!DOCTYPE HTML> | ||
| + | <html> | ||
| + | <head> | ||
| + | <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> | ||
| + | <title>Show File Data</title> | ||
| + | <style type='text/css'> | ||
| + | body { | ||
| + |     font-family: sans-serif; | ||
| + | } | ||
| + | </style> | ||
| + | <script type='text/javascript'> | ||
| + | |||
| + |     function loadFile() { | ||
| + |         var input, file, fr; | ||
| + | |||
| + |         if (typeof window.FileReader !== 'function') { | ||
| + |             bodyAppend("p", "The file API isn't supported on this browser yet."); | ||
| + |             return; | ||
| + |         } | ||
| + | |||
| + |         input = document.getElementById('fileinput'); | ||
| + |         if (!input) { | ||
| + |             bodyAppend("p", "Um, couldn't find the fileinput element."); | ||
| + |         } | ||
| + |         else if (!input.files) { | ||
| + |             bodyAppend("p", "This browser doesn't seem to support the `files` property of file inputs."); | ||
| + |         } | ||
| + |         else if (!input.files[0]) { | ||
| + |             bodyAppend("p", "Please select a file before clicking 'Load'"); | ||
| + |         } | ||
| + |         else { | ||
| + |             file = input.files[0]; | ||
| + |             fr = new FileReader(); | ||
| + |             fr.onload = receivedText; | ||
| + |             fr.readAsText(file); | ||
| + |         } | ||
| + | |||
| + |         function receivedText() { | ||
| + |             showResult(fr, "Text"); | ||
| + | |||
| + |             fr = new FileReader(); | ||
| + |             fr.onload = receivedBinary; | ||
| + |             fr.readAsBinaryString(file); | ||
| + |         } | ||
| + | |||
| + |         function receivedBinary() { | ||
| + |             showResult(fr, "Binary"); | ||
| + |         } | ||
| + |     } | ||
| + | |||
| + |     function showResult(fr, label) { | ||
| + |         var markup, result, n, aByte, byteStr; | ||
| + | |||
| + |         markup = []; | ||
| + |         result = fr.result; | ||
| + |         for (n = 0; n < result.length; ++n) { | ||
| + |             aByte = result.charCodeAt(n); | ||
| + |             byteStr = aByte.toString(16); | ||
| + |             if (byteStr.length < 2) { | ||
| + |                 byteStr = "0" + byteStr; | ||
| + |             } | ||
| + |             markup.push(byteStr); | ||
| + |         } | ||
| + |         bodyAppend("p", label + " (" + result.length + "):"); | ||
| + |         bodyAppend("pre", markup.join(" ")); | ||
| + |     } | ||
| + | |||
| + |     function bodyAppend(tagName, innerHTML) { | ||
| + |         var elm; | ||
| + | |||
| + |         elm = document.createElement(tagName); | ||
| + |         elm.innerHTML = innerHTML; | ||
| + |         document.body.appendChild(elm); | ||
| + |     } | ||
| + | |||
| + | </script> | ||
| + | </head> | ||
| + | <body> | ||
| + | <form action='#' onsubmit="return false;"> | ||
| + | <input type='file' id='fileinput'> | ||
| + | <input type='button' id='btnLoad' value='Load' onclick='loadFile();'> | ||
| + | </form> | ||
| + | </body> | ||
| + | </html> | ||
| + | |||
| </pre> | </pre> | ||
Latest revision as of 12:50, 25 May 2011
setTimeout with a shorter delay
Wednesday, 2010-03-09, 13:45 -0800
On Sunday, somebody with the nickname {g} was on irc.mozilla.org asking about the behavior of setTimeout. In particular, he wanted to divide up work into a bunch of pieces in a way that allowed the user to interact with the page while the work was happening, and was doing this by doing a piece of the work, and then making a setTimeout call to continue the work. (In some cases, this could also be done using workers.) Unfortunately for him, setTimeout in most browsers doesn't allow a delay less than about 10 milliseconds (it forces any smaller delays to be longer), so the work wasn't finishing as fast as it could. (Chrome has changed this to 2 milliseconds, though, and apparently had some problems with it.)
A while ago, Jeff Walden suggested to me that Web pages could get the equivalent of setTimeout, with a real zero delay, using postMessage. This turns out to be relatively straightforward:
    // Only add setZeroTimeout to the window object, and hide everything
    // else in a closure.
    (function() {
        var timeouts = [];
        var messageName = "zero-timeout-message";
        // Like setTimeout, but only takes a function argument.  There's
        // no time argument (always zero) and no arguments (you have to
        // use a closure).
        function setZeroTimeout(fn) {
            timeouts.push(fn);
            window.postMessage(messageName, "*");
        }
        function handleMessage(event) {
            if (event.source == window && event.data == messageName) {
                event.stopPropagation();
                if (timeouts.length > 0) {
                    var fn = timeouts.shift();
                    fn();
                }
            }
        }
        window.addEventListener("message", handleMessage, true);
        // Add the one thing we want added to the window object.
        window.setZeroTimeout = setZeroTimeout;
    })();
I wrote a demo page that demonstrates that this is significantly faster than setTimeout(0). On a Firefox nightly 100 iterations of setZeroTimeout take about 10-20 milliseconds most of the time, but occasionally longer; on a WebKit build I have it takes about 4-6 milliseconds, but occasionally a bit longer. (We should probably investigate the performance difference here.) In comparison, in Firefox and on non-Chromium-based WebKit, the setTimeout version takes about a second (though perhaps even longer on Windows).
Update (2010-03-12): My numbers were on Linux. Boris tells me that on Mac, it's the opposite: Gecko is faster than Safari or Chrome.
function doCalculation() 
{ 
   //do your thing for a short time 
   //figure out how complete you are 
   var percent_complete=.... 
   return percent_complete; 
} 
function pump() 
{ 
   var percent_complete=doCalculation(); 
   //maybe update a progress meter here! 
   //carry on pumping? 
   if (percent_complete<100) 
   { 
      setTimeout(pump, 50); 
   } 
} 
//start the calculation 
pump(); 
arr.sort(function(e1,e2) {
    return e1>e2 ? 1:(e1<e2 ? -1:0)
})
arr.sort(function(e1,e2) {
    return e1>e2 ? -1:(e1<e2 ? 1:0)
})
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Show File Data</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
</style>
<script type='text/javascript'>
    function loadFile() {
        var input, file, fr;
        if (typeof window.FileReader !== 'function') {
            bodyAppend("p", "The file API isn't supported on this browser yet.");
            return;
        }
        input = document.getElementById('fileinput');
        if (!input) {
            bodyAppend("p", "Um, couldn't find the fileinput element.");
        }
        else if (!input.files) {
            bodyAppend("p", "This browser doesn't seem to support the `files` property of file inputs.");
        }
        else if (!input.files[0]) {
            bodyAppend("p", "Please select a file before clicking 'Load'");
        }
        else {
            file = input.files[0];
            fr = new FileReader();
            fr.onload = receivedText;
            fr.readAsText(file);
        }
        function receivedText() {
            showResult(fr, "Text");
            fr = new FileReader();
            fr.onload = receivedBinary;
            fr.readAsBinaryString(file);
        }
        function receivedBinary() {
            showResult(fr, "Binary");
        }
    }
    function showResult(fr, label) {
        var markup, result, n, aByte, byteStr;
        markup = [];
        result = fr.result;
        for (n = 0; n < result.length; ++n) {
            aByte = result.charCodeAt(n);
            byteStr = aByte.toString(16);
            if (byteStr.length < 2) {
                byteStr = "0" + byteStr;
            }
            markup.push(byteStr);
        }
        bodyAppend("p", label + " (" + result.length + "):");
        bodyAppend("pre", markup.join(" "));
    }
    function bodyAppend(tagName, innerHTML) {
        var elm;
        elm = document.createElement(tagName);
        elm.innerHTML = innerHTML;
        document.body.appendChild(elm);
    }
</script>
</head>
<body>
<form action='#' onsubmit="return false;">
<input type='file' id='fileinput'>
<input type='button' id='btnLoad' value='Load' onclick='loadFile();'>
</form>
</body>
</html>
