如何使用Q library创建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>
答案 0 :(得分:11)
这不会导致堆栈溢出,因为promises会破坏堆栈,但它会泄漏内存。如果在node.js中运行相同的代码,则会收到以下错误:
FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
这里发生的事情是,正在创建一个非常长的嵌套承诺链,每个承诺都在等待下一个。你需要做的是找到一种方法来压扁这个链,这样只有一个顶级的承诺会被返回,等待目前代表一些实际工作的最内在的承诺。
最简单的解决方案是在顶层构建一个新的promise并使用它来打破递归:
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");
});
虽然这个解决方案有点不优雅,但您可以确定它适用于所有承诺实施。
某些承诺实现(例如来自npm的promise,您可以从https://www.promisejs.org/下载作为独立库)正确检测此案例并将承诺链合并为单个承诺。这样做可以让您不必提及顶级功能返回的承诺(即立即致电.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实现具有此优化,并且没有任何计划实现它。
答案 1 :(得分:2)
以下是您尝试做的最简单的实现,如果这有效,那么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>