Periodically calling a function, stack growing indefinite

58 Views Asked by At

I have the following JavaScript code and noticed the stack is growing indefinitely.

https://jsfiddle.net/uok7vz9b/

(function () {
  async function poll() {
    try {
        // await fetch ...
    } catch (err) {
      console.error("Error: ", err);
    } finally {
      console.trace();
      start();
    }
  }

  function start() {
    setTimeout(poll, 2000);
  }

  document.addEventListener("DOMContentLoaded", poll, {once: true});
})();

First output is:

console.trace() _display:118:15
    poll https://fiddle.jshell.net/_display/?editor_console=true:118
    (Async: EventListener.handleEvent)
    <anonymous> https://fiddle.jshell.net/_display/?editor_console=true:127
    <anonymous> https://fiddle.jshell.net/_display/?editor_console=true:128

Second output:

console.trace() _display:118:15
    poll https://fiddle.jshell.net/_display/?editor_console=true:118
    (Async: setTimeout handler)
    start https://fiddle.jshell.net/_display/?editor_console=true:124
    poll https://fiddle.jshell.net/_display/?editor_console=true:119
    (Async: EventListener.handleEvent)
    <anonymous> https://fiddle.jshell.net/_display/?editor_console=true:127
    <anonymous> https://fiddle.jshell.net/_display/?editor_console=true:128

... and so on.

I don't really understand why, because I though setTimeout does not block and just starts a new timer. So I expected the poll function to finish, and then it is fired again with a clean stack.

I have looked around and not really found any good examples for "execute function once on page load and then periodically". I don't want to use setInterval because long running AJAX calls could then exceed the timeout. But the timeout should rather be the minimum time between two calls of poll.

1

There are 1 best solutions below

2
spender On BEST ANSWER

This is a result of async stack traces. I have no idea if it's possible to blow the async stack or whether there are measures in the browser to limit the depth of the async stack preventing overflow/leaks.

While the following code does the same thing:

(function() {
  const delay = (duration) => new Promise(r => setTimeout(() => r(), duration))
  async function poll() {
    for (;;) {
      try {
        // await fetch ...
      } catch (err) {
        console.error("Error: ", err);
      } finally {
        console.trace();
      }
      await delay(2000)
    }

  }


  document.addEventListener("DOMContentLoaded", poll, {
    once: true
  });
})();

It does not add to the async stack.