Вы должны заключить рекурсивный вызов функции в
setTimeout,
setImmediate или же
process.nextTick
чтобы дать node.js возможность очистить стек. Если вы этого не сделаете и есть много циклов без какого-либо реального вызова асинхронной функции или если вы не дождетесь обратного вызова, RangeError: Maximum call stack size exceededэто будет неизбежно .
Есть много статей о «Возможном асинхронном цикле». Вот один .
А теперь еще несколько примеров кода:
var condition = false,
max = 1000000;
function potAsyncLoop( i, resume ) {
if( i < max ) {
if( condition ) {
someAsyncFunc( function( err, result ) {
potAsyncLoop( i+1, callback );
});
} else {
potAsyncLoop( i+1, resume );
}
} else {
resume();
}
}
potAsyncLoop( 0, function() {
...
});
Это правильно:
var condition = false,
max = 1000000;
function potAsyncLoop( i, resume ) {
if( i < max ) {
if( condition ) {
someAsyncFunc( function( err, result ) {
potAsyncLoop( i+1, callback );
});
} else {
setTimeout( function() {
potAsyncLoop( i+1, resume );
}, 0 );
}
} else {
resume();
}
}
potAsyncLoop( 0, function() {
...
});
Теперь ваш цикл может стать слишком медленным, потому что мы теряем немного времени (один обход браузера) на раунд. Но необязательно делать колл setTimeoutв каждом раунде. Обычно это нормально делать это каждые 1000 раз. Но это может отличаться в зависимости от размера вашего стека:
var condition = false,
max = 1000000;
function potAsyncLoop( i, resume ) {
if( i < max ) {
if( condition ) {
someAsyncFunc( function( err, result ) {
potAsyncLoop( i+1, callback );
});
} else {
if( i % 1000 === 0 ) {
setTimeout( function() {
potAsyncLoop( i+1, resume );
}, 0 );
} else {
potAsyncLoop( i+1, resume );
}
}
} else {
resume();
}
}
potAsyncLoop( 0, function() {
...
});