На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: ElcnU, ANDLL, fatalist
  
> async.series
    Написал свою упрощенную версию async.series, но не работает :wall:
    ExpandedWrap disabled
      function foreach(arrayFuncs, iterFunc, callback) {
          var done = false,
              isRunning = false,
              i = 0,
              iterator = arrayFuncs.values();
       
          while (!isRunning && !done) {
              var item = iterator.next();
              var elem = null;
              if (!item.done) {
                  elem = {task: item.value, key: i++};
              }
       
              if (elem === null) {
                  done = true;
                  if (isRunning) callback(null);
                  return;
              }
              isRunning = true;
              
              iterFunc(elem.task, elem.key, err => {
                  isRunning = false;
                  if (err) {
                      done = true;
                      callback(err);
                  }
              });
          }
      }
       
      function series(tasks, callback) {
          var results = [];
          foreach(tasks,
              function (task, key, callback) {
                  task((err, result) => {
                      results[key] = result;
                      callback(err);
                  });
              },
              function (err) {
                  callback(err, results);
              }
          );
      }
       
      // test
      series([
          cb => setTimeout(() => cb(null, 'task 1'), 1000),
          cb => setTimeout(() => cb(null, 'task 2'), 1000)
      ],
      (err, results) => {
          if (err) return console.log(err);
          console.log(results);
      });
       
      console.log('test series');

    Где я накосячил? :blink:
      надо спросить у масты, он ответит.
        Сам все порешал! :D
        Через цикл чет я завис, потом решу для практики. А пока рекурсивное решение, рекурсивно мыслить проще да и значительно сократилась сама функция :D
        ExpandedWrap disabled
          function foreach(arrayFuncs, iterFunc, callback) {
              var iterator = arrayFuncs.values();
              var key = 0;
           
              function next() {
                  var item = iterator.next();
                  if (item.done) return callback(null);
           
                  iterFunc(item.value, key++, err => {
                      if (err) return callback(err);
                      next();
                  });
              }
           
              next();
          }
           
          function series(tasks, callback) {
              var results = [];
              foreach(tasks,
                  function (task, key, callback) {
                      task((err, result) => {
                          results[key] = result;
                          callback(err);
                      });
                  },
                  function (err) {
                      callback(err, results);
                  }
              );
          }
           
          // test
          series([
              cb => setTimeout(() => {console.log('1'); cb(null, 'task 1')}, 1000),
              cb => setTimeout(() => {console.log('2'); cb(null, 'task 2')}, 1000),
              cb => setTimeout(() => {console.log('3'); cb(null, 'task 3')}, 1000)
          ],
          (err, results) => {
              if (err) return console.log(err);
              console.log(results);
          });
           
          console.log('test series');
          // test series
          // 1
          // 2
          // 3
          // [ 'task 1', 'task 2', 'task 3' ]
        Сообщение отредактировано: Cfon -
          Теперь надо реализовать async.parallel и async.waterfall.
          Новичок! Я тебя вижу не прячься! :D

          - Маста а может сначало объясните как реализована series? :huh:
          Сообщение отредактировано: Cfon -
            Ок Маста сам все порешал :lool: вот накалбасил по аналогии :D
            ExpandedWrap disabled
              function foreach(arrayFuncs, iterFunc, callback) {
                  var done = false,
                      running = 0,
                      iterator = arrayFuncs.values(),
                      key = 0;
               
                  while (!done) {
                      var item = iterator.next();  
                      if (item.done) {
                          done = true;
                          if (running <= 0) callback(null);
                          return;
                      }
                      running++;
               
                      iterFunc(item.value, key++, err => {
                          running--;
                          if (err) {
                              done = true;
                              callback(err);
                          }
                          else if (running <= 0) {
                              callback(null);
                          }
                      });
                  }
              }
               
              function parallel(tasks, callback) {
                  var results = [];
                  foreach(tasks,
                      function (task, key, callback) {
                          task((err, result) => {
                              results[key] = result;
                              callback(err);
                          });
                      },
                      function (err) {
                          callback(err, results);
                      }
                  );
              }
               
              //test parallel
              parallel([
                  cb => {console.log('1'); setTimeout(() => cb(null, 'task 1'), 2000)},
                  cb => {console.log('2'); setTimeout(() => cb(new Error('Error!!!'), 'task 2'), 2000)},
                  cb => {console.log('3'); setTimeout(() => cb(null, 'task 3'), 2000)}
              ],
              (err, results) => {
                  if (err) return console.log(err);
                  console.log(results);
              });
               
              console.log('test parallel');
              /*
              1
              2
              3
              test parallel
              Error: Error!!!
                  at Timeout.setTimeout [as _onTimeout] (/Users/cfon/workspace/JavaScript/examples/async-parallel.js:49:50)
                  at ontimeout (timers.js:436:11)
                  at tryOnTimeout (timers.js:300:5)
                  at listOnTimeout (timers.js:263:5)
                  at Timer.processTimers (timers.js:223:10)
              [ 'task 1', 'task 2', 'task 3' ]
              */

            Код прекрасно работает все выводит, но тут что-то не так поскольку результат всеравно возвращается даже если возникает ощибка :blink:
            Где я накосячил? :wacko:
            Кстати сама функция parallel один в один series т.е. она не изменилась! А все модификации каснулись функции foreach.
            Сообщение отредактировано: Cfon -
              Новичок! Апладирую стоя :good:
              Я проверил твой код и должен сказать, что ситуация тут весьма запутана!
              Чтобы разобраться в этих хитросплетениях колбэк вызовов требуется не малая интелектуальная нагрузка :D
              Так что пока я могу предложить тебе следущий кастыль, мы можем прервать цепочку колбэков используя следущий трюк:
              ExpandedWrap disabled
                function parallel(tasks, callback) {
                    var results = [];
                 
                    callback = once(callback); //<-- переопределяем колбэк на однократный запуск
                 
                    foreach(tasks,
                        function (task, key, callback) {
                            task((err, result) => {
                                results[key] = result;
                                callback(err);
                            });
                        },
                        function (err) {
                            callback(err, results);
                        }
                    );
                }
                 
                function once(cb) {
                    return function (...args) {
                        if (cb === null) return;
                        var callback = cb;
                        cb = null;
                        callback(...args);
                    };
                }

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

              - Вау Маста! Это работает! :o
              Но как ты понимаешь таски, которые были запущены они все отрабатывают, тут просто не вызывается конечный колбэк функции parallel в случае ошибки и как следствие результат не выводится на экран.
              Сообщение отредактировано: Cfon -
                А вот другое решение, поскольку именно переменная running используется для контроля запуска финального колбэка, все что нам надо при ошибке сделать ее значение например undefined :D
                ExpandedWrap disabled
                  function foreach(arrayFuncs, iterFunc, callback) {
                      var done = false,
                          running = 0,
                          iterator = arrayFuncs.values(),
                          key = 0;
                      
                      while (!done) {
                          var item = iterator.next();  
                          if (item.done) {
                              done = true;
                              if (running <= 0) callback(null);
                              break;
                          }
                          running++;
                   
                          iterFunc(item.value, key++, err => {
                              running--;
                              if (err) {
                                  running = undefined; //<-- тадам!
                                  callback(err);
                              }
                              else if (running <= 0) {
                                  callback(null);
                              }
                          });
                      }
                  }
                Сообщение отредактировано: Cfon -
                  Новая версия функции series :D
                  Убрал ненужную сложность кода, теперь проще понять как все работает:
                  ExpandedWrap disabled
                    function series(tasks, callback) {
                        const results = [];
                        var iterator = tasks.values();
                        var key = 0;
                     
                        function next() {
                            var item = iterator.next();
                            if (item.done) return callback(null, results);
                          
                            item.value((err, result) => {
                                results[key++] = result;
                                if (err) return callback(err);
                                next();
                            });
                        }
                     
                        next();
                    };

                  Новичок переделай async.parallel и async.waterfall.
                  Сообщение отредактировано: Cfon -
                    Masta, ты молодец.
                    Юзаешь эту гребанную технологию JS.
                    Ждем когда она сойдет на нет.
                      Цитата nash @
                      Masta, ты молодец.
                      Юзаешь эту гребанную технологию JS.
                      Ждем когда она сойдет на нет.

                      Ждать придется долго, скорее никогда.

                      Добавлено
                      - Маста вот как будет async.parallel :blush:
                      ExpandedWrap disabled
                        function parallel(tasks, callback) {
                            var running = 0,
                                key = 0,
                                iterator = tasks.values(),
                                results = [];
                            
                            while (true) {
                                var item = iterator.next();  
                                if (item.done) {
                                    if (running <= 0) callback(null, results);
                                    return;
                                }
                                running++;
                                item.value((err, result) => {
                                    if (err) {
                                        running = Infinity;
                                        return callback(err);
                                    }
                                    running--;
                                    results[key++] = result;
                                    if (running <= 0) callback(null, results);
                                });
                            }  
                        }

                      - Да еще одно замечание мне пришлось включить модуль babel-polifill т.к в массивы Node 10 не имеют values().

                      Добавлено
                      Цитата Cfon @
                      - Да еще одно замечание мне пришлось включить модуль babel-polifill т.к в массивы Node 10 не имеют values().

                      Необязательно вспомни цикл for of. Что там юзается? Да-да неудивляйся тоже итератор! tasks.values() можно заменить на tasks[Symbol.iterator]() :D

                      - Вау Маста! Работает! :thanks:
                      Сообщение отредактировано: Cfon -
                        А вот и async.waterfall :D
                        ExpandedWrap disabled
                          function waterfall(tasks, callback) {
                              var iterator = tasks.values();
                              var item = iterator.next();
                           
                              function next(args) {
                                  args.push((err, ...args) => {
                                      item = iterator.next();
                                      if (err || item.done) return callback(err, ...args);
                                      next(args);
                                  });
                           
                                  item.value(...args);
                              }
                           
                              next([]);
                          };
                           
                          //test waterfall
                          waterfall([
                              cb => {console.log('1'); setTimeout(() => cb(null, 'one', 'two'), 1000)},
                              (arg1, arg2, cb) => {console.log('2'); setTimeout(() => cb(null, `${arg1} ${arg2} three`), 1000)},
                              (arg, cb) => {console.log('3'); setTimeout(() => cb(null, `${arg} done`), 1000)}
                          ],
                          (err, result) => {
                              if (err) return console.log(err);
                              console.log(result);
                          });
                           
                          console.log('test waterfall');
                          // 1
                          // test waterfall
                          // 2
                          // 3
                          // one two three done

                        с waterfall пришлось немного повозится, поскольку в ней результаты выполнения предыдущей функции должны передаваться следущей. Как видите я заюзал ...args и в конце каждой функции добавлял колбэк согласно требованию.
                          Чем отличается async.series от async.waterfall?
                          Series позволяет выполнять задачи последовательно, передавая результат своей работы в массив results конечного колбэка. При этом каждый результат будет сохраняться в соответствующей ячейке.
                          Waterfall также выполняет задачи последовательно, но с тем отличием, что результаты предыдущей задачи могут передаваться следующей. Последняя задача передаёт свой результат в конечный колбэк.
                          Сами задачи в обоих случаях могут выполняться как синхронно так и асинхронно.
                          Понятно, что в случае только синхронных задач юзать series, waterfall, как впрочем parallel не имеет смысла по очевидной причине :D
                          Сообщение отредактировано: Cfon -
                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                          0 пользователей:


                          Рейтинг@Mail.ru
                          [ Script execution time: 0,0578 ]   [ 15 queries used ]   [ Generated: 29.03.24, 01:07 GMT ]