Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does JavaScript promise create memory leaks when not rejected or resolved?

I'm in a situation where I need execute async functions in "parallel", and continue program execution with the best result. Thus I wrote something like this :

var p = [];

for (var i = 0; i < 10; ++i) (function (index) {
  p.push(new Promise(function (resolve, reject) {
    setTimeout(function () {
      var success = Math.random() > 0.7;

      console.log("Resolving", index, "as", success ? "success" : "failure");

      success && resolve(index);
    }, Math.random() * 5000 + 200);
  }));
})(i);

Promise.race(p).then(function (res) {
  console.log("FOUND", res);
}).catch(function (err) {
  console.log("ERROR", err);
});

Now, I'm wondering if this is good practice when working with promises? Is not resolving or rejecting them more often then anything create memory leaks? Are they all eventually GC'ed every time?

like image 445
Yanick Rochon Avatar asked Jul 20 '15 16:07

Yanick Rochon


People also ask

What happens if Promise is not resolved or rejected?

A promise is just an object with properties in Javascript. There's no magic to it. So failing to resolve or reject a promise just fails to ever change the state from "pending" to anything else. This doesn't cause any fundamental problem in Javascript because a promise is just a regular Javascript object.

What happens if a Promise never resolves?

Since you don't call resolve() , that code will never execute and thus the console will not print "done". The body of the Promise is, however, executed immediately, and since it's empty, there is nothing left for the program to execute!

What causes memory leak in Javascript?

The main cause of memory leaks in an application is due to unwanted references. The garbage collector finds the memory that is no longer in use by the program and releases it back to the operating system for further allocation.

What happens when you reject a Promise Javascript?

If it rejects, it is rejected with the reason from the first promise that was rejected. Returns a new Promise object that is rejected with the given reason. Returns a new Promise object that is resolved with the given value.


1 Answers

The only reason this causes a memory leak is because p is a global variable. Set p = null; at the end, or avoid using a global variable:

var console = { log: function(msg) { div.innerHTML += msg + "<br>"; }};

Promise.race(new Array(10).fill(0).map(function(entry, index) {
  return (function(index) {
    return new Promise(function(resolve) {
      setTimeout(function() {
        var success = Math.random() > 0.7;
        console.log((success? "R":"Not r") + "esolving "+ index +".");
        success && resolve(index);
      }, Math.random() * 5000 + 200);
    });
  })(index);
})).then(function (res) {
    console.log("FOUND: " + res);
}).catch(function (err) {
    console.log("ERROR: " + err);
});
<div id="div"></div>

Promises are garbage and cycle collected when nothing holds on to them, which is when your JS and the browser (here setTimeout) have no references to them anymore.

As soon as one entry in p succeeds or something fails, whichever is sooner, and setTimeout will have let go of everything after 5.2 seconds. JavaScript will then happily garbage collect the promises whether they've been resolved, rejected, or neither. No harm.

The only thing you want to avoid is garbage collecting rejected promises, as that is likely to trigger a browser warning because it is indicative of a web programming bug.

Of course, there's a 30% chance none of them resolve, in which case this will leak (until you close the tab).

Is this good design?

I think it depends on what the functions are doing. If they are lightweight, then I don't see a problem. If they are doing heavy computations or have side-effects, then they hopefully come with some API to cancel the operations, to help save on resources.

like image 82
jib Avatar answered Oct 24 '22 08:10

jib