gitweb.js: use setTimeout rather than setInterval in blame_incremental.js

If there is a possibility that your logic could take longer to execute than the interval time, it is recommended that you recursively call a named function using window.setTimeout rather than window.setInterval. Therefore instead of using setInterval as an alternate way of invoking handleResponse (because some web browsers call onreadystatechange only once per each distinct state, and not for each server flush), use setTimeout and reset it from handleResponse. As a bonus this allows us to get rid of timer if it turns out that web browser calls onreadystatechange on each server flush. While at it get rid of `xhr' global variable, creating it instead as local variable in startBlame and passing it as parameter, and of `pollTimer' global variable, passing it as member of xhr object (xhr.pollTimer). Signed-off-by: Jakub Narebski <jnareb@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jakub Narebski committed May 27, 2011 at 15:50 UTC 42ab5d40deb6dff37705ce5aa16ac69d859a04e1
1 file changed +40 -15
gitweb/static/js/blame_incremental.js
+40 -15
@@ -29,7 +29,6 @@
29 /* ............................................................ */
30 /* utility/helper functions (and variables) */
31
32 -var xhr; // XMLHttpRequest object
32 var projectUrl; // partial query + separator ('?' or ';')
33
34 // 'commits' is an associative map. It maps SHA1s to Commit objects.
@@ -431,8 +430,6 @@ var endRe = /^END ?([^ ]*) ?(.*)/;
430 var curCommit = new Commit();
431 var curGroup = {};
432
434 -var pollTimer = null;
435 -
433 /**
434 * Parse output from 'git blame --incremental [...]', received via
435 * XMLHttpRequest from server (blamedataUrl), and call handleLine
@@ -533,26 +530,34 @@ function processData(unprocessed, nextReadPos) {
530 * Handle XMLHttpRequest errors
531 *
532 * @param {XMLHttpRequest} xhr: XMLHttpRequest object
533 + * @param {Number} [xhr.pollTimer] ID of the timeout to clear
534 *
537 - * @globals pollTimer, commits
535 + * @globals commits
536 */
537 function handleError(xhr) {
538 errorInfo('Server error: ' +
539 xhr.status + ' - ' + (xhr.statusText || 'Error contacting server'));
540
543 - clearInterval(pollTimer);
541 + if (typeof xhr.pollTimer === "number") {
542 + clearTimeout(xhr.pollTimer);
543 + delete xhr.pollTimer;
544 + }
545 commits = {}; // free memory
546 }
547
548 /**
549 * Called after XMLHttpRequest finishes (loads)
550 *
550 - * @param {XMLHttpRequest} xhr: XMLHttpRequest object (unused)
551 + * @param {XMLHttpRequest} xhr: XMLHttpRequest object
552 + * @param {Number} [xhr.pollTimer] ID of the timeout to clear
553 *
552 - * @globals pollTimer, commits
554 + * @globals commits
555 */
556 function responseLoaded(xhr) {
555 - clearInterval(pollTimer);
557 + if (typeof xhr.pollTimer === "number") {
558 + clearTimeout(xhr.pollTimer);
559 + delete xhr.pollTimer;
560 + }
561
562 fixColorsAndGroups();
563 writeTimeInterval();
@@ -563,9 +568,13 @@ function responseLoaded(xhr) {
568 * handler for XMLHttpRequest onreadystatechange event
569 * @see startBlame
570 *
566 - * @globals xhr
571 + * @param {XMLHttpRequest} xhr: XMLHttpRequest object
572 + * @param {Number} xhr.prevDataLength: previous value of xhr.responseText.length
573 + * @param {Number} xhr.nextReadPos: start of unread part of xhr.responseText
574 + * @param {Number} [xhr.pollTimer] ID of the timeout (to reset or cancel)
575 + * @param {Boolean} fromTimer: if handler was called from timer
576 */
568 -function handleResponse() {
577 +function handleResponse(xhr, fromTimer) {
578
579 /*
580 * xhr.readyState
@@ -614,6 +623,19 @@ function handleResponse() {
623 // did we finish work?
624 if (xhr.readyState === 4) {
625 responseLoaded(xhr);
626 + return;
627 + }
628 +
629 + // if we get from timer, we have to restart it
630 + // otherwise onreadystatechange gives us partial response, timer not needed
631 + if (fromTimer) {
632 + setTimeout(function () {
633 + handleResponse(xhr, true);
634 + }, 1000);
635 +
636 + } else if (typeof xhr.pollTimer === "number") {
637 + clearTimeout(xhr.pollTimer);
638 + delete xhr.pollTimer;
639 }
640 }
641
@@ -629,11 +651,11 @@ function handleResponse() {
651 * Called from 'blame_incremental' view after loading table with
652 * file contents, a base for blame view.
653 *
632 - * @globals xhr, t0, projectUrl, div_progress_bar, totalLines, pollTimer
654 + * @globals t0, projectUrl, div_progress_bar, totalLines
655 */
656 function startBlame(blamedataUrl, bUrl) {
657
636 - xhr = createRequestObject();
658 + var xhr = createRequestObject();
659 if (!xhr) {
660 errorInfo('ERROR: XMLHttpRequest not supported');
661 return;
@@ -652,8 +674,9 @@ function startBlame(blamedataUrl, bUrl) {
674 xhr.prevDataLength = -1; // used to detect if we have new data
675 xhr.nextReadPos = 0; // where unread part of response starts
676
655 - xhr.onreadystatechange = handleResponse;
656 - //xhr.onreadystatechange = function () { handleResponse(xhr); };
677 + xhr.onreadystatechange = function () {
678 + handleResponse(xhr, false);
679 + };
680
681 xhr.open('GET', blamedataUrl);
682 xhr.setRequestHeader('Accept', 'text/plain');
@@ -661,7 +684,9 @@ function startBlame(blamedataUrl, bUrl) {
684
685 // not all browsers call onreadystatechange event on each server flush
686 // poll response using timer every second to handle this issue
664 - pollTimer = setInterval(xhr.onreadystatechange, 1000);
687 + xhr.pollTimer = setTimeout(function () {
688 + handleResponse(xhr, true);
689 + }, 1000);
690 }
691
692 /* end of blame_incremental.js */