Will await
ing a Promise
which neither resolves nor rejects (never settle/unfulfilled) cause a memory leak?
I got curious about this while looking at React hooks with slorber/awesome-debounce-promise that creates new promises, but only settles the last one of them, thus leaving many/most unsettle/unfulfilled.
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.
You can use the async/await syntax or call the . then() method on a promise to wait for it to resolve. Inside of functions marked with the async keyword, you can use await to wait for the promises to resolve before continuing to the next line of the function.
The static Promise. reject function returns a Promise that is rejected. For debugging purposes and selective error catching, it is useful to make reason an instanceof Error .
This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future. A Promise is in one of these states: pending: initial state, neither fulfilled nor rejected.
Preface (you probably know this!):
await
is syntactic sugar for using promise callbacks. (Really, really, really good sugar.) An async
function is a function where the JavaScript engine builds the promise chains and such for you.
Answer:
The relevant thing isn't so much whether the promise is settled, but whether the promise callbacks (and the things they refer to / close over) are retained in memory. While the promise is in memory and unsettled, it has a reference to its callback functions, keeping them in memory. Two things make those references go away:
In the normal case, the consumer of a promise hooks up handlers to the promise and then either doesn't keep a reference to it at all, or only keeps a reference to it in a context that the handler functions close over and not elsewhere. (Rather than, for instance, keeping the promise reference in a long-lived object property.)
Assuming the debounce implementation releases its reference to the promise that it's never going to settle, and the consumer of the promise hasn't stored a reference somewhere outside this mutual-reference cycle, then the promise and the handlers registered to it (and anything that they hold the only reference for) can all be garbage collected once the reference to the promise is released.
That requires a fair bit of care on the part of the implementation. For instance (thanks Keith for flagging this up), if the promise uses a callback for some other API (for instance, addEventListener
) and the callback closes over a reference to the promise, since the other API has a reference to the callback, that could prevent all references to the promise from being released, and thus keep anything the promise refers to (such as its callbacks) in memory.
So it'll depend on the implementation being careful, and a bit on the consumer. It would be possible to write code that would keep references to the promises, and thus cause a memory leak, but in the normal case I wouldn't expect the consumer to do that.
I did some testing using the following structure:
function doesntSettle() { return new Promise(function(resolve, reject) { // Never settle the promise }); } let awaited = 0; let resolved = 0; async function test() { awaited++; await doesntSettle(); resolved++; } setInterval(() => { for (let i = 0; i < 100; ++i) { test(); } }, 1);
Implemented here: https://codesandbox.io/s/unsetteled-awaited-promise-memory-usage-u44oc
Running just the result frame in Google Chrome showed continuously increasing memory usage in dev tools Memory tab (but not under the Performance/JS heap tab), indicating a leak. Running this but resolving the promises did not leak.
Running this increased memory usage for me increased by 1-4MB/second. Stopping it and running the GC did not free up any of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With