question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Timers in the Window context prevent Node.js process from exit and window object from GC clean-up

See original GitHub issue

If FetchExternalResources and ProcessExternalResources contain 'script' and page scripts set timers, Node.js process does not exit.

Test code 1 (page scripts with some possible timers are under user’s control):

const jsdom = require('jsdom');

jsdom.env({
  html: `
    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title></title>
      </head>
      <body>
        <script>
          window.document.title = 'Scripted Title';
          window.setInterval(() => {}, 1000);
        </script>
      </body>
    </html>
  `,
  done: (err, window) => {
    console.log(window.document.title);
  },
  features: {
    FetchExternalResources: ['script'],
    ProcessExternalResources: ['script']
  },
});

After logged output the script just freezes. However, if all timers are cleaned up, the script exits properly:

const jsdom = require('jsdom');

jsdom.env({
  html: `
    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title></title>
      </head>
      <body>
        <script>
          window.document.title = 'Scripted Title';
          window.setInterval(() => {}, 1000);
        </script>
      </body>
    </html>
  `,
  done: (err, window) => {
    console.log(window.document.title);

    let id = window.setTimeout(() => {}, 0);
    console.log(id);
    while (id) {
      window.clearTimeout(id);
      id--;
    }

    id = window.setInterval(() => {}, 0);
    console.log(id);
    while (id) {
      window.clearInterval(id);
      id--;
    }
  },
  features: {
    FetchExternalResources: ['script'],
    ProcessExternalResources: ['script']
  },
});

Test code 2 (page scripts with many timers are beyond easy user’s control):

const jsdom = require('jsdom');

jsdom.env({
  url: 'http://www.nytimes.com/',
  done: (err, window) => {
    console.log(window.document.title);
  },
  features: {
    FetchExternalResources: ['script'],
    ProcessExternalResources: ['script']
  },
});

After logged output the script just freezes as well. And if all timers are cleaned up, the script exits properly too:

const jsdom = require('jsdom');

jsdom.env({
  url: 'http://www.nytimes.com/',
  done: (err, window) => {
    console.log(window.document.title);

    let id = window.setTimeout(() => {}, 0);
    console.log(id);
    while (id) {
      window.clearTimeout(id);
      id--;
    }

    id = window.setInterval(() => {}, 0);
    console.log(id);
    while (id) {
      window.clearInterval(id);
      id--;
    }
  },
  features: {
    FetchExternalResources: ['script'],
    ProcessExternalResources: ['script']
  },
});

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
domeniccommented, May 24, 2016

There’s no way to execute code in the future without keeping the process in which that code will execute alive. So documenting seems like the way to go here.

0reactions
vsemozhetbytcommented, May 25, 2016

Well, I am not a native English speaker and my English is bad, so maybe I should not suggest a large amount of the text. However, something like that (with your additions and corrections) may be helpful:

It should be taken into consideration, that timers in the page scripts (set by window.setImmediate(), window.setInterval() and window.setTimeout()) could prevent window object from being GC-ed and thereby could cause memory leaks. These timers also could interfere with main process eventloop and prevent the script from natural exit after eventloop drain. To mitigate these risks, window.close() should be called after each document processing completion if page scripts execution is activated.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Timers in Node.js
The Timers module in Node.js contains functions that execute code after a set period ... This function is similar to window. ... The...
Read more >
Timers | Node.js v19.3.0 Documentation
This object is created internally and is returned from setTimeout() and setInterval() . It can be passed to either clearTimeout() or clearInterval() in...
Read more >
Node.js v19.3.0 Documentation
Creates a new CallTracker object which can be used to track if functions were called a specific number of times. The tracker.verify() must...
Read more >
The Node.js Event Loop, Timers, and process.nextTick()
timers : this phase executes callbacks scheduled by setTimeout() and setInterval() . · pending callbacks: executes I/O callbacks deferred to the next loop ......
Read more >
Process | Node.js v19.3.0 Documentation
The 'beforeExit' event is emitted when Node.js empties its event loop and has no additional work to schedule. Normally, the Node.js process will...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found