Ответ 1
Это не приведет к переполнению стека, потому что promises сломает стек, но это приведет к утечке памяти. Если вы запустите этот же код в node.js, вы получите сообщение об ошибке:
FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
Что здесь происходит, так это то, что создается действительно длинная цепочка вложенных promises, каждая из которых ждет следующего. То, что вам нужно сделать, это найти способ сгладить эту цепочку, чтобы получить только одно обещание высшего уровня, которое возвращается, ожидая самого внутреннего обещания, которое в настоящее время представляет собой реальную работу.
разрыв цепи
Самое простое решение - построить новое обещание на верхнем уровне и использовать его для разрыва рекурсии:
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 из npm, которые вы можете скачать как автономную библиотеку из https://www.promisejs.org/) правильно определить этот случай и свернуть цепочку promises в одно обещание. Это работает, если вы не держите ссылку на обещание, возвращенное функцией верхнего уровня (т.е. Наберите .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");
});
К сожалению, ни одна из встроенных реализаций обещаний не имеет такой оптимизации, и никто не планирует ее реализовывать.