Рекурсия с асинхронными функциями и промисами Bluebird в JavaScript

В JavaScript у меня есть четыре набора функций:

Set_A
  synchronousFunction_One
  synchronousFunction_Two
  synchronousFunction_Three
  synchronousFunction_Four
  synchronousFunction_Five

Set_B
   objectA.asynchronousFunction_One
   objectA.asynchronousFunction_Two
                       .
                       .
                       .
   objectA.asynchronousFunction_N

Set_C
   objectA.asynchronousFunction_Three
   objectA.asynchronousFunction_Four
                       .
                       .
                       .
   objectA.asynchronousFunction_M

Set_D
  synchronousFunction_Six

Мне нужно, чтобы каждый набор запускался один за другим, в определенном порядке и с определенными ограничениями:

  1. Ни одна из функций в Set_A не может быть вызвана до тех пор, пока не будет разрешено обещание извне.
  2. Синхронные функции в Set_A повторяются, вызываясь каждый раз.
  3. После каждого вызова функции в Set_A итератор приостанавливается и ждет разрешения шагов 4 и 5, приведенных ниже, перед переходом к следующему элементу в Set_A.
  4. Set_B повторяется, и каждый метод вызывается один раз, открывая несколько асинхронных подключений к Интернету.
  5. После того, как все вызовы на шаге 4 разрешены, Set_C повторяется, и каждый метод вызывается один раз, снова открывая несколько асинхронных подключений к Интернету.
  6. Как только все вызовы на шаге 5 разрешены, итератор на шаге 1, описанном выше, переходит к следующему элементу в Set_A.

Итак, по сути, то, что мы делаем здесь, это ожидание разрешения некоторого внешнего обещания, затем мы вызываем функцию, чтобы, так сказать, «запустить насос». Затем мы перебираем часть интерфейса объекта, «независимую часть» — методы, которые можно вызывать в любое время. Затем мы перебираем другую часть интерфейса этого объекта, «зависимую часть» (т. е. ни один из методов в зависимой части не закроется должным образом, если все методы в независимой части не закрылись хотя бы один раз). И наконец, мы вызываем функцию очистки. Как только это будет сделано, мы начнем со следующего элемента в Set_A и снова запустим насос.

Самый высокий уровень абстракции, опять же с использованием библиотеки Bluebird Promise, выглядит так:

function doAllTheThings( externalPromise ) {

  var Set_A, Set_B, Set_C; // Array<Function>

  return new Promise( promiseToDoAllTheThings );
  function promiseToDoAllTheThings( resolve, reject ) {

    externalPromise.then( go );

    function go() {

      var primePump = Set_A.pop();
      if ( !primePump ) return;

      primePump();
      callEverythingInSet_B()
        .then( callEverythingInSet_C )
        .then( cleanUp )
      ;
    }

    function callEverythingInSet_B() {

      var promises = [];
      for ( var index in Set_B )
        promises.push( Set_B[index]() );
      return Promise.all( promises );
    }

    function callEverythingInSet_C() {

      var promises = [];
      for ( var index in Set_C )
        promises.push( Set_C[index]() );
      return Promise.all( promises );
    }

    function cleanUp() {

      // Do stuff
      go();
    }
  }
}

Мне очень тяжело с этим; Вчера я потратил несколько часов на факторинг и рефакторинг, каждый раз не получая ожидаемого поведения. Я все еще работаю над своим реальным кодом; так что, может быть, я найду странную точку с запятой, которая сводит меня с ума, или что-то в этом роде.

Но тем временем я решил опубликовать это здесь и спросить, правильно ли я понимаю библиотеку Bluebird Promise? Учитывая приведенный выше код, следует ли ожидать поведения, которое я описал? Если нет, можете ли вы предоставить какой-нибудь демонстрационный код, который будет?

Мои фолликулы благодарны вам.


person Community    schedule 05.02.2015    source источник


Ответы (1)


Да, похоже, это работает. Тем не менее, несколько советов:

  • не помещайте вызов функции go() в cleanUp. Свяжите его сразу после .then(cleanUp) в самой функции go, сделав очевидным рекурсивный характер (и сохранив локальные ссылки).
  • Тот

    return new Promise( promiseToDoAllTheThings );
    function promiseToDoAllTheThings( resolve, reject ) {
    

    неправильно. Возвращенное обещание никогда не разрешается и не отклоняется, вы не используете эти обратные вызовы. Однако вы даже не должны использовать их здесь вообще. Просто опустите этот promiseToDoAllTheThings и сделайте только

     return externalPromise.then( go );
    

    напрямую.

  • Вместо этих простых циклов for попробуйте использовать Array::map, так будет лучше :-)
  • вы можете использовать Promise.each вместо твоя go петля
person Bergi    schedule 05.02.2015
comment
Берги за победу! Спасибо, мужик. Вы подключены к Bluebird? Я вижу тебя повсюду, чувак. (Сейчас я набираю все это в Code Pen; когда все будет готово, я дам ссылку для потомков.) - person ; 06.02.2015
comment
Кроме того, вы случайно не знаете, что работает лучше, ванильный for или Array::map? Это код на стороне сервера, поэтому я стараюсь придерживаться того, что быстро дымится. (Таким образом, выбор Bluebird.) - person ; 06.02.2015
comment
Нет, @BenjaminGruenbaum и @Esailija — авторы bluebird, я еще не участвовал в этом, но работаю только над реализацией собственных (академических) обещаний. - person Bergi; 06.02.2015
comment
циклы for работают немного быстрее, поскольку им не нужно вызывать функции. Однако это микрооптимизация, направленная на чистый код. Сетевые подключения — это то, что ограничивает производительность вашего метода. - person Bergi; 06.02.2015
comment
@LMiz: Ваше предлагаемое редактирование лучше было бы комментарием - person Bergi; 06.02.2015
comment
Вам не кажется, что ссылка будет скрыта? - person ; 06.02.2015
comment
Неа. Я бы даже проголосовал за это, тогда комментарий отображается не свернутым. Если вы хотите сделать это действительно видимым, лучше отредактируйте свой вопрос, - person Bergi; 06.02.2015
comment
Учитывая три, я бы оставил это в комментарии здесь. Я добавлю его самостоятельно, далее. - person ; 06.02.2015
comment
Вот макет CodePen основных изменений, которые предлагает @Bergi: http://codepen.io/anon/pen/WbXwVv Прекрасно работает! Спасибо чувак! - person ; 06.02.2015
comment
@Bergi не уверен, где еще вас найти - хотел спросить, знаете ли вы в #promises, и пригласить вас спрятаться. Время от времени у нас там происходят интересные дискуссии, и мы будем признательны за ваши отзывы :) - person Benjamin Gruenbaum; 06.02.2015