使用递归承诺来阻止内存泄漏
Stop memory leaks with recursive promises
如何使用Q库创建JavaScript Promise
的递归链?以下代码无法在Chrome中完成:
<html>
<script src="q.js" type="text/javascript"></script>
<script type="text/javascript">
//Don't keep track of a promises stack for debugging
//Reduces memory usage when recursing promises
Q.longStackJumpLimit = 0;
function do_stuff(count) {
if (count==1000000) {
return;
}
if (count%10000 == 0){
console.log( count );
}
return Q.delay(1).then(function() {
return do_stuff(count+1);
});
}
do_stuff(0)
.then(function() {
console.log("Done");
});
</script>
</html>
这不会导致堆栈溢出,因为promise会破坏堆栈,但会泄漏内存。如果你在node.js中运行同样的代码,你会得到一个错误,上面写着:
FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
这里发生的事情是,一个非常长的嵌套承诺链正在创建,每个承诺都在等待下一个承诺。你需要做的是找到一种方法来压平这个链条,这样只有一个顶级的承诺会被返回,等待目前代表一些实际工作的最内部的承诺。
断链
最简单的解决方案是在顶层构建一个新的承诺,并使用它来打破递归:
var Promise = require('promise');
function delay(timeout) {
return new Promise(function (resolve) {
setTimeout(resolve, timeout);
});
}
function do_stuff(count) {
return new Promise(function (resolve, reject) {
function doStuffRecursion(count) {
if (count==1000000) {
return resolve();
}
if (count%10000 == 0){
console.log( count );
}
delay(1).then(function() {
doStuffRecursion(count+1);
}).done(null, reject);
}
doStuffRecursion(count);
});
}
do_stuff(0).then(function() {
console.log("Done");
});
尽管这个解决方案有些不雅,但您可以确信它在所有promise实现中都能工作。
then/promise现在支持尾部递归
一些promise实现(例如来自npm的promise,您可以从https://www.promisejs.org/)正确检测这种情况,并将承诺链分解为单个承诺。如果您不保留对顶级函数返回的promise的引用(即,立即调用它的.then
,不要保留它(,这将起作用。
好:
var Promise = require('promise');
function delay(timeout) {
return new Promise(function (resolve) {
setTimeout(resolve, timeout);
});
}
function do_stuff(count) {
if (count==1000000) {
return;
}
if (count%10000 == 0){
console.log( count );
}
return delay(1).then(function() {
return do_stuff(count+1);
});
}
do_stuff(0).then(function() {
console.log("Done");
});
错误:
var Promise = require('promise');
function delay(timeout) {
return new Promise(function (resolve) {
setTimeout(resolve, timeout);
});
}
function do_stuff(count) {
if (count==1000000) {
return;
}
if (count%10000 == 0){
console.log( count );
}
return delay(1).then(function() {
return do_stuff(count+1);
});
}
var thisReferenceWillPreventGarbageCollection = do_stuff(0);
thisReferenceWillPreventGarbageCollection.then(function() {
console.log("Done");
});
不幸的是,没有一个内置的promise实现有这种优化,也没有任何实现它的计划。
下面是您尝试做的事情的最简单的实现,如果它有效,那么q库就有问题,否则就会有一些深层的javascript问题:
<html>
<script type="text/javascript">
function do_stuff(count) {
if (count==1000000) {
return done();
}
if (count%1000 == 0){
console.log( count );
}
return setTimeout(function() { do_stuff(count+1); }, 0);
}
do_stuff(0);
function done() {
console.log("Done");
};
</script>
</html>
相关文章:
- 重复应用 d3 转换导致的内存泄漏
- IE7中的blockUI插件内存泄漏25kb
- Javascript闭包-如何防止内存泄漏
- jQuery Draggable:内存泄漏
- "检测到可能的EventEmitter内存泄漏”;使用Gulp+Watchify+Factor捆绑包
- 在Dojo类中递归调用setTimeout时是否存在内存泄漏
- 是内存泄漏
- 将处理程序留在img.onload上是内存泄漏
- 具有并发sse连接的node.js内存泄漏
- 简单对象的Javascript内存泄漏
- WeakMap是否会将我从父/子关系的内存泄漏中拯救出来
- Javascript绘制画布内存泄漏
- Node.js”;检测到EventEmitter内存泄漏”;
- 正在清理内存泄漏
- 递归Javascript对象是否会导致任何问题(内存泄漏)
- Angular JS$编译服务导致$watch内存泄漏
- 如何防止和防范闭包内存泄漏
- 跟踪 JavaScript 内存泄漏的工具
- 页面刷新后javascript内存泄漏有问题吗?为什么?
- XMLHttpRequest循环内存泄漏