Search code examples
javascriptgarbage-collectionpromisebluebird

Can a Bluebird promise be GC'd before completion?


Assume you have the following code:

function startApp() {
    createReallyLongRunningPromise();

    // intensive work with memory
}

Is there any way that the created promise will get GC'd before it's done or is it guaranteed to finish working as long as the process is live?

If it's relevant, the long-running promise's body returns a promise of itself:

function createReallyLongRunningPromise() {
    return new Promise(resolve => {
        // Stuff

        return promiseCreatedDuringStuff;
    });
}

Solution

  • Is there any way that the created promise will get GC'd before it's done

    No. Assuming the async operation still has not completed and still has the ability to resolve or reject the promise, then it retains some sort of reference to the promise object so it cannot be GCed.

    A promise object is just a normal Javascript object. It will be garbage collected exactly the same as any other Javascript object which is when no active, reachable code has a reference to the promise object any more. A reference can be held to the promise either by the asynchronous operation while it's underway or by the initiator of the async operation and user of the promise (most likely by both).

    As long as an asynchronous operation is underway and some part of that asynchronous operation has a reference to either the resolve or reject callbacks from the promise executor function, then there's a reference to the promise object that is still alive and it is simply not eligible for garbage collection.

    Once the asynchronous operation completes, calls its completion callback and does whatever it intends to do with resolve() or reject() and all .then() handlers have been called, then the async operation itself probably no longer has a reference to the promise object. So, when the promise object would be eligible for garbage collection would depend entirely upon the calling code and how long it retains a reference to the promise. If that promise was stored in a variable, then the reference will stay alive until either the variable itself is GCed or until the variable is assigned some other value.

    or is it guaranteed to finish working as long as the process is live?

    There is no guarantee that a promise is ever resolved or ever eligible for garbage collection. A promise that is stored in a long lasting variable (e.g. a global or some persistent state) will simply never be eligible for garbage collection.

    Similarly a promise that starts an async operation that itself retains a reference to the resolve or reject functions from the promise executor, but never actually completes and stays in scope itself will just create a dangling promise that never resolves or rejects.

    As an example if you used a webSocket connection to send a message to a remote host and you set up a promise to resolve when a specific response message comes back from the remote host, but the response message never arrives back, then that promise would just be waiting forever for the response message to come back. Because it's still "active" and the response message could still arrive some time in the future, nothing is eligible for garbage collection.

    That promise dangles forever and is not garbage collected. Now, in the real world, one should probably implement some sort of timeout that would reject the promise if the response didn't come back in some sort of time frame and that would prevent the dangling promise lasting forever because it was still waiting for a response.

    And, if the caller holds onto a reference to the promise forever, then the promise will never be eligible for garbage collection. Consider this trivial example in the global scope (or any lasting scope like a loaded module scope):

    var p = new Promise(function(resolve) {
        setTimeout(resolve, 1000);
    });
    
    p.then(function() {
        console.log("timer fired, promise resolved");
    });
    

    If this variable p never goes out of scope and thus lasts forever, then the promise help in it will never be eligible for garbage collection. This is no different than any other object assigned to p.


    Now, consider this example:

    var p = new Promise(function(resolve) {
        setTimeout(resolve, 1000);
    });
    
    p.then(function() {
        console.log("timer fired, promise resolved");
    });
    // clear variable p
    p = null;
    

    Here, the variable p has been cleared so it no longer retains a reference to the promise object. As such, the promise will be eligible for garbage collection as soon as the p.then() handler has run because no active code will any longer have a reference to that promise`.


    Now, most of the time you don't have to manually clear a promise variable like this. For example, if the code above was just written like this:

    new Promise(function(resolve) {
        setTimeout(resolve, 1000);
    }).then(function() {
        console.log("timer fired, promise resolved");
    });
    

    Then, no lasting variable p is created in the first place.


    Or, if this was inside a function scope that itself would finish such as:

    function runDelay() {
        var p = new Promise(function(resolve) {
            setTimeout(resolve, 1000);
        });
    
        p.then(function() {
            console.log("timer fired, promise resolved");
        });
    }
    
    runDelay();
    

    Then, the variable p itself goes out of scope after the promise fires so both it and the promise itself are eligible for GC after the promise resolves.