这不会堆栈溢出,因为承诺会破坏堆栈,但它会泄漏内存。如果您在 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");
});
虽然这个解决方案有点不优雅,但您可以确定它适用于所有 Promise 实现。
then/promise 现在支持尾递归
一些 Promise 实现(例如来自npm的 Promise,您可以从https://www.promisejs.org/作为独立库下载)正确地检测到这种情况并将 Promise 链折叠成一个 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 实现有这种优化,也没有任何计划来实现它。