Рекурсия и связывание обещаний в Javascript

Как мне написать рекурсивный цикл для правильного выполнения обещаний по порядку? Я пробовал с Promise.all (Array.map (function () {})); что не соответствует моим потребностям, так как эти шаги нужно выполнять последовательно. Я пробовал использовать индивидуальное обещание, потому что нашел здесь , но и здесь есть проблемы.

Обещание:

var promiseFor = (function(condition, action, value) {
    var promise = new Promise(function(resolve, reject) {
        if(!condition(value)) {
            return;
        }
        return action(value).then(promiseFor.bind(null, condition, action));
    });
    return promise;
});

Проблема с этим в том, что кажется, что он останавливается на самом глубоком рекурсивном вызове, не возвращаясь для продолжения выполнения цикла для правильного завершения.

Например: в PHP такой код:

function loopThrough($source) {
    foreach($source as $value) {
        if($value == "single") {
            //do action
        } else if($value == "array") {
            loopThrough($value);
        }
    }
}

Если я пропущу, скажем, структуру папок, а «один» означает файл, он распечатает все файлы. Но обещание, что я использую стопы в первом тупике.

РЕДАКТИРОВАТЬ: добавлен bluebird, чтобы узнать, может ли он чем-нибудь помочь, все то же самое. Вот текущий код цикла

var runSequence = (function(sequence, params) { 
    return Promise.each(sequence, function(action) {
        console.log(action['Rusiavimas'] + ' - ' + action['Veiksmas']);
        if(params.ButtonIndex && params.ButtonIndex != action['ButtonIndex']) {
            return Promise.resolve();
        }

        if(action['Veiksmas'].charAt(0) == '@') {
            var act = action['Veiksmas'];
            var actName = act.substr(0, act.indexOf(':')).trim();
            var actArg = act.substr(act.indexOf(':')+1).trim();

            /* This one is the code that figures out what to do and
               also calls this function to execute a sub-sequence. */
            return executeAction(actName, actArg, params);
        } else {
            sendRequest('runQuery', action['Veiksmas']);
        }
    });
});

У меня есть 3 последовательности, каждая из которых состоит из 5 действий. Первая и вторая последовательности имеют следующую последовательность как третье действие. Вот результат, который я получаю (числа означают, какая последовательность):

1 - @PirmasVeiksmas
1 - @AntrasVeiksmas
1 - @Veiksmas: list_two
2 - @PirmasVeiksmas
2 - @AntrasVeiksmas
2 - @Veiksmas: list_three
3 - @PirmasVeiksmas
3 - @AntrasVeiksmas
3 - @TreciasVeiksmas
3 - @KetvirtasVeiksmas
3 - @PenktasVeiksmas

Как вы можете видеть, он входит в следующую последовательность и продолжается, как и должен, но после завершения третьей последовательности следует возобновить вторую последовательность и завершить первую. Но он останавливается, как только попадает в первый тупик в рекурсии.

EDIT2: пример Codepen того, что у меня есть сейчас, и с визуальным представлением того, что происходит: Ссылка Codepen

Результат должен быть:

fa1
second
sa1
third
ta1
ta2
ta3
sa3
fa3

person IntoDEV    schedule 01.04.2016    source источник
comment
этот PHP-код асинхронный?   -  person dandavis    schedule 01.04.2016
comment
@dandavis нет, это был просто пример, я просто не могу понять, почему цепочка обещаний останавливается, когда она попадает в первый тупик, и должна вернуться на шаг (рекурсия), чтобы продолжить предыдущее.   -  person IntoDEV    schedule 01.04.2016
comment
Пожалуйста, всегда указывайте, где вы нашли код - укажите автора и поместите ссылку! Кстати, вот оригинал (в котором тоже нет ошибки)   -  person Bergi    schedule 01.04.2016
comment
@Bergi для одного цикла, но когда дело доходит до рекурсивной цепочки, это не работает. Из того, что я понял, кажется, что первый самый глубокий конец при достижении «схлопывается» обратно к началу, завершая цепочку обещаний, тем самым останавливая любой предыдущий цикл.   -  person IntoDEV    schedule 01.04.2016
comment
@IntoDEV: проблема в том, что он не сворачивается обратно, обещания, которые возвращает ваша функция, никогда не выполняются. Здесь совершенно неправильно использовать конструктор Promise. Вот почему я хотел, чтобы вы связали исходный код этого кода, чтобы я мог ударить его автора.   -  person Bergi    schedule 01.04.2016
comment
@ Берги хех, ты знаешь какие-нибудь альтернативы или как мне это сделать? Поскольку главным приоритетом здесь является пошаговое выполнение и ожидание, действия имеют запросы ajax, диалоговые окна пользователя и должны ждать, прежде чем они будут выполнены, чтобы продолжить следующие шаги.   -  person IntoDEV    schedule 01.04.2016
comment
@IntoDEV: просто исправьте функцию promiseFor. Работает при правильном написании.   -  person Bergi    schedule 01.04.2016


Ответы (2)


Выполнение группы действий в последовательности может быть выполнено с .reduce() вместо .map().

Вот пример:

// Helper function that creates a Promise that resolves after a second
const delay = (value) => new Promise(resolve => setTimeout(() => resolve(value), 1000));

const arr = [1, 2, 3, 4, 5];

// concurrent resolution with `.map()` and `Promise.all()`
Promise.all(arr.map(delay))
  .then(console.log.bind(console)); // [1, 2, 3, 4, 5] after a second.

// sequential resolution with `.reduce()`

arr.reduce((promise, current) => 
             promise
               .then(() => delay(current))
               .then(console.log.bind(console)), 
           Promise.resolve());
// second wait, 1, second wait, 2...

Если я правильно понял ваши требования, вам не нужна рекурсия Promise, это просто способ последовательного запуска Promises. .reduce() может помочь вам в этом проще.

Процесс сокращения превращает [1,2,3,4,5] в:

Promise.resolve()
  .then(() => delay(1))
  .then(console.log.bind(console))
  .then(() => delay(2))
  .then(console.log.bind(console))
  .then(() => delay(3))
  .then(console.log.bind(console))
  .then(() => delay(4))
  .then(console.log.bind(console))
  .then(() => delay(5))
  .then(console.log.bind(console))

Обратите внимание, что если вы хотите получить доступ ко всем результатам, вам нужно проделать немного больше работы. Но я оставлю это в качестве упражнения для читателя :)

person Madara's Ghost    schedule 24.10.2016
comment
Спасибо, но я уже решил эту проблему с помощью другого вопроса о переполнении стека. Оказывается, у меня были проблемы с jQuery 2.x, так как я перешел на 3.x, проблема решилась сама собой. Я не использовал jQuery в самих обещаниях, чтобы сохранить их независимость, но просто из-за того, что jQuery в моем коде, испортил пару вещей. Изменить: если вам интересно, вот сообщение ссылка - person IntoDEV; 26.10.2016

Таким образом, это устраняет проблемы с вашим кодом кода, поэтому он дает желаемый результат.

var sequences = {
  first: ['fa1', 'second', 'fa3'],
  second: ['sa1', 'third', 'sa3'],
  third: ['ta1', 'ta2', 'ta3']
};

var loopThrough = (function(sequence) {
  return sequence.forEach(function(action) {
    return doAction(action);
  });
});

var doAction = (function(action) {
  var promise = new Promise(function(resolve, reject) {
    console.log(action);
    if(action == 'second' || action == 'third') {
      //recurse into sub-sequence
      return loopThrough(sequences[action]);
    } else {
      //do something here
    }
    resolve();
  });
  return promise;
});

loopThrough(sequences.first);
person Arthur Cinader    schedule 22.10.2016