1

I found what looks to be a bug in Microsoft Edge. The callback to setInterval() is sometimes called while print() is executing. This results in 2 JavaScript functions running in parallel which shouldn't be allowed, right?

The behavior can be observed with this simple test app.

index.html:

<!DOCTYPE html>
<html>    
  <head>
    <script src="script.js"></script>
  </head>
  <body>
    <input type="button" onclick="printPage()" value="Print"/>
  </body>
</html>

script.js:

var isPrinting = false;

setInterval(function tick() {
  if (isPrinting) {
    alert('Interval callback called conurrently with click handler!');
  }  
}, 10);

function printPage() {
  isPrinting = true;
  try {
    print();
  }
  finally {
    isPrinting = false;
  }
}

https://plnkr.co/edit/eKMQEHjRiXzl1vzjzIEN

When I click on the "Print" button, I do not expect to see an alert, but I do observe an alert.

Environment: Microsoft Edge 38.14393.0.0, Windows 10

Is this a bug or do I not understand something?

Mike
  • 11
  • 4
  • 2
    Well using an alert is a bad idea for debugging. What are you really trying to do? What is the purpose of the interval? – epascarello Dec 14 '16 at 22:34
  • 1
    A ran into an issue in a real application and narrowed the issue down to the test case in the plunkr. You can replace the alert() with a console.log() if you wish. I just thought the alert made it easier for the reader. – Mike Dec 14 '16 at 22:45

2 Answers2

0

Yes this is a confirmed bug. I'm a bit surprised that no one has apparently reported this before me.

https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10230373/

Mike
  • 11
  • 4
0

Here is how I am working around the bug.

(function () {
    var isPrinting = false;
    var _setInterval = window.setInterval;
    var _print = window.print;
    var queue = [];

    window.setInterval = function (fn, delay) {
        var params;
        if (arguments.length > 2) {
            params = arguments.slice(2);
        }
        var wrapper = function () {
            if (!isPrinting) {
                fn.apply(null, params);
            }
            else {
                //console.log('queuing...');
                // Queue only one callback per setInterval() call. This mimics Chrome and IE11's behavior.
                if (!wrapper.queued) {
                    wrapper.queued = true;
                    queue.push(wrapper);
                }
            }
        };
        _setInterval(wrapper, delay);
    };

    window.print = function () {
        //console.log('print begin');
        isPrinting = true;
        try {
            _print.apply(this, arguments);
        }
        finally {
            //console.log('print end');
            isPrinting = false;
            if (queue.length > 0) {
                var _queue = queue; // Save the contents of the queue so that we have them when the timeout callback executes.
                setTimeout(function () {
                    _queue.forEach(function (wrapper) {
                        wrapper.queued = false;
                        wrapper();
                    });
                });
                queue = [];
            }
        }
    }
})();
Mike
  • 11
  • 4