Javascript recursion, memory leak?

122
April 28, 2021, at 08:40 AM

I am trying to implement a class that will perform an action once every second, it works as expected but I am unsure about memory leaks. This code I'm writing will have an uptime of several months.

Will the code below lead to memory leaks since it's technically recursion which never ends?

class Algorithm{
  constructor(){
    //there will be many more things in this constructor
    //which is why this is a class
    const pidTimer = (r,e=0,y=Date.now()-r) => {
      this.someFunction();
      const now = Date.now();
      const dy = now-y;
      const err = e+r-dy
      const u = err*0.2;
      //console.log(dy)
      setTimeout(()=>{pidTimer(r,err,now)},r+u);
    }
    pidTimer(1000);
  }
  someFunction = () => {}
}
Answer 1

It's not the kind of recursion that has any stack accumulation since the previous pidTimer() function call returns before the setTimeout() fires and calls picTimer() again. I don't even call this recursion (it's scheduled repeated calling), but that's more a semantic issue.

So, the only place I see there could be some memory leak or excess usage would be inside of this.someFunction(); and that's only because you don't show us the code there to evaluate it and see what it does. The code you show us for pidTimer() itself has no issues on its own.

Answer 2

modern async primitive

There's nothing "wrong" with the current function you have, however I think it could be improved significantly. JavaScript offers a modernized asynchrony atom, Promise and new syntax support async/await. These are preferred over stone-age setTimeout and setInterval as you can easily thread data through asynchronous control flow, stop thinking in terms of "callbacks", and avoid side effects -

class Algorithm {
  constructor() {
    ...
    this.runProcess(...)
  }
  async runProcess(...) { // async
    while (true) {        // loop instead of recursion
      await sleep(...)    // sleep some amount of time
      this.someFunction() // do work
      ...                 // adjust timer variables
    }
  }
}

sleep is a simple function which resolves a promise after a specified milliseconds value, ms -

function sleep(ms) {
  return new Promise(r => setTimeout(r, ms)) // promise
}

async iterables

But see how this.someFunction() doesn't return anything? It would be nice if we could capture the data from someFunction and make it available to our caller. By making runProcess an async generator and implementing Symbol.asyncIterator we can easily handle asynchrony and stop side effects -

class Algorithm {
  constructor() {
    ...
    this.data = this.runProcess(...)  // assign this.data
  }
  async *runProcess(...) {            // async generator
    while (true) {
      await sleep(...)    
      yield this.someFunction()       // yield
      ...
    }
  }
  [Symbol.asyncIterator]() {          // iterator
    return this.data
  }
}

Now the caller has control over what happens as the data comes in from this.someFunction. Below we write to console.log but you could easily swap this for an API call or write to file system -

const foo = new Algorithm(...)
for await (const data of foo)
  console.log("process data", data) // or API call, or write to file system, etc

additional control

You can easily add control of the process to by using additional data members. Below we swap out while(true) with a conditional and allow the caller to stop the process -

class Algorithm {
  constructor() {
    ...
  }
  async *runProcess(...) {
    this.running = true       // start
    while (this.running) {    // conditional loop
      ...
    }
  }
  haltProcess() {
    this.running = false      // stop
  }
  ...
}

demo

Here's a functioning demo including the concepts above. Note we only implement halt here because run is an infinite generator. Manual halting is not necessary for finite generators. Verify the results in your own browser by running the snippet -

class Algorithm {
  async *run() {
    this.running = true
    while(this.running) {
      await sleep(1000)
      yield this.someFunction()
    }
  }
  halt() {
    this.running = false
  }
  someFunction() {
    return Math.random()
  }
  [Symbol.asyncIterator] = this.run
}
function sleep(ms) {
  return new Promise(r => setTimeout(r, ms))
}
async function main() {
  const foo = new Algorithm          // init
  setTimeout(_ => foo.halt(), 10000) // stop at some point, for demo
  for await (const x of foo)         // iterate
    console.log("data", x)           // log, api call, write fs, etc
  return "done"                      // return something when done
}
main().then(console.log, console.error) // "done"

data 0.3953947360028206
data 0.18754462176783115
data 0.23690422070864803
data 0.11237466374294014
data 0.5123244720637253
data 0.39818889343799635
data 0.08627407687877853
data 0.3861902404922477
data 0.8358471443658225
data 0.2770336562516085
done
READ ALSO
java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/**/cache/storage/CAPTURE-20210427_125521.jpg

java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/**/cache/storage/CAPTURE-20210427_125521.jpg

I am developing a flutter app in which I'm using webview_flutter pluginNow inside the webview I want upload a file from the device

147
Php datetime convert to ISO 8601

Php datetime convert to ISO 8601

I have an app that would call an Amazon api, the datetime requirements should be an ISO 8601 formatI tried the Carbon like

67
Implementing a dual sidebar angular

Implementing a dual sidebar angular

I am trying to implement a sidebar within a sidebar for an angular projectBasically when I click on projects, a new sidebar should replace the current sidebar with a completely different set of options

34
Reading second row from excel using apache poi

Reading second row from excel using apache poi

I am trying to read the first and second row of the excel , I am not able to figure out the way to read the second row of excel and compare the values at the first execution

108