如何创建的JavaScript的递归链Promise
s的的Q库 ? 下面的代码无法在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>
这不会堆栈溢出承诺打破堆栈,但它会导致内存泄漏。 如果您在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");
});
虽然这种解决方案是有点不雅,你可以肯定它会在所有承诺的实现工作。
然后/承诺现在支持尾递归
有些承诺实现(例如承诺从NPM,您可以下载从一个独立的库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");
});
不幸的是,没有内置的承诺,实现有这种优化,并没有要实现它的任何计划。
下面是最简单的实现你想要做什么,如果这个工程再有就是用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>