Consider a function to make a HTTP request until it succeeds:
function fetch_retry(url) {
return fetch(url).catch(function(error) {
return fetch_retry(url);
});
}
As the number of failure increases, does the memory consumption increase?
I guess the callstack height is O(1) but I don't know if the clojure context (growing over time) is retained.
Edit:
Case2
function fetch_retry(url) {
return fetch(url).catch(function(error) {
//return fetch_retry(url)+1;
return fetch_retry(url).then(res=>{res.body+='a'});
});
}
(assume that fetch resolves to a number) The constant 1 should be in memory because it is used later.
Case3
function fetch_retry(url, odd_even) {
return fetch(url).catch(function(error) {
//return fetch_retry(url, 1-odd_even)+odd_even;
return fetch_retry(url, 1-odd_even).then(res=>{res.body+=str(odd_even)});
});
}
fetch_retry('http://..', 0)
By alternating 0 and 1, the compiler cannot reuse the operand.
Edit:
No one explained about the case2 and 3, so I run the experiment. Surprisingly the memory increases in all scenarios.
function f(n) {
return new Promise(function (resolve, reject) {
gc();
if(n==10000) return resolve(0);
// else return f(n+1).then(value=>value+n);
// else return f(n+1).then(value=>value);
else return resolve(f(n+1));
});
}
f(0).then(res => {
console.log('result: ', res);
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`memory: ${Math.round(used * 100) / 100} MB`);
}).finally(()=>console.log('finished'));
And run with
node --stack_size=999999 --expose_gc test.js
Note that I ran gc() every time to prevent delayed GC.
- With n=1000, memory: 4.34 MB
- With n=10000, memory: 8.95 MB
5MB for 9000 stacks = 590 bytes per call.
One possibility is that resolve
function in each stack is retained. To remove this,
function f(n) {
return new Promise(function (resolve, reject) {
gc();
if(n==10000) resolve(0);
// else return f(n+1).then(value=>value+n);
else return f(n+1).then(value=>value);
// else return f(n+1);
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`memory: ${Math.round(used * 100) / 100} MB`);
});
}
f(0);
- With n=1000, memory: 4.12 MB
- With n=10000, memory: 7.07 MB
So the stack is flat but the memory is not clean as we think?