setInterval() callback called concurrently with print()

Confirmed Issue #10230373 • Assigned to Sanket J.

Details

Author
sfsadfsd s.
Created
Dec 15, 2016
Privacy
This issue is public.
Found in
  • Microsoft Edge
Found in build #
38.14393
Reports
Reported by 1 person

Sign in to watch or report this issue.

Steps to reproduce

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

Click on the “Print” button.

Observed: An alert popup is displayed.
Expected: An alert popup is NOT displayed.

Environment:

  • Microsoft Edge 38.14393.0.0
  • Windows 10 Version 1607 (OS Build 14393.576)

Attachments

0 attachments

    Comments and activity

    • Microsoft Edge Team

      Changed Assigned To to “Travis L.”

      Changed Assigned To from “Travis L.” to “Sanket J.”

      Changed Status to “Confirmed”

    • For what it’s worth, this is how I am working around the issue.

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

      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('deferring: ' + wrapper.id);
                  // Defer only one callback per setInterval() call. This mimics Chrome and IE11's behavior.
                  if (!index[wrapper.id]) {
                      var deferred = function () {
                          fn.apply(null, params);
                      };
                      deferred.id = wrapper.id;
                      index[wrapper.id] = deferred;
                      queue.push(deferred);
                  }
              }
          };
          wrapper.id = id++;
          _setInterval(wrapper, delay);
      };
      
      window.print = function () {
          //console.log('print begin');
          isPrinting = true;
          try {
              _print.apply(this, arguments);
          }
          finally {
              //console.log('print end');
              isPrinting = false;
              var _queue = queue; // Save the contents of the queue so that we have them when the timeout callback executes.
              setTimeout(function () {
                  _queue.forEach(function (fn) {
                      //console.log('running ' + fn.id);
                      fn();
                  });
              });
              queue = [];
              index = {};
          }
      }
      

      })();

    • For what it’s worth, this is how I am working around the issue.

      (function () {
          var isPrinting = false;
          var _setInterval = window.setInterval;
          var _print = window.print;
          var id = 0;
          var queue = [];
          var index = {};
          
          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('deferring: ' + wrapper.id);
                      // Defer only one callback per setInterval() call. This mimics Chrome and IE11's behavior.
                      if (!index[wrapper.id]) {
                          var deferred = function () {
                              fn.apply(null, params);
                          };
                          deferred.id = wrapper.id;
                          index[wrapper.id] = deferred;
                          queue.push(deferred);
                      }
                  }
              };
              wrapper.id = id++;
              _setInterval(wrapper, delay);
          };
      
          window.print = function () {
              //console.log('print begin');
              isPrinting = true;
              try {
                  _print.apply(this, arguments);
              }
              finally {
                  //console.log('print end');
                  isPrinting = false;
                  var _queue = queue; // Save the contents of the queue so that we have them when the timeout callback executes.
                  setTimeout(function () {
                      _queue.forEach(function (fn) {
                          //console.log('running ' + fn.id);
                          fn();
                      });
                  });
                  queue = [];
                  index = {};
              }
          }
      })();

    You need to sign in to your Microsoft account to add a comment.

    Sign in