определение окончания асинхронных операций JavaScript - PullRequest
3 голосов
/ 28 июля 2011

Если у меня есть функция, которая передала эту функцию:

function(work) {
   work(10);
   work(20);
   work(30);
}

(Может быть любое количество work вызовов с любым номером в них.)

work выполняет некоторую асинхронную активность - скажем, для этого примера это просто timeout. У меня есть полный контроль над тем, что work делает по завершении этой операции (и, вообще говоря, по ее определению).

Как лучше всего определить, когда все вызовы на work сделаны?


Мой текущий метод увеличивает счетчик, когда вызывается работа, и уменьшает его, когда она завершается, и вызывает событие all work done, когда счетчик равен 0 (это проверяется после каждого уменьшения). Тем не менее, я волнуюсь, что это может быть какое-то состояние гонки. Если это не так, покажите мне, почему, и это был бы отличный ответ.

Ответы [ 3 ]

2 голосов
/ 28 июля 2011

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

Важно помнить, причина, по которой это будет работать, заключается в том, что Javascript выполняется в одном потоке .Это верно для всех браузеров и node.js AFAIK.

Основываясь на вдумчивых комментариях ниже, решение работает, потому что цикл событий JS будет выполнять функции в следующем порядке:

  1. функция (работа)
  2. работа(10)
  3. счетчик ++
  4. запуск асинхронной функции
  5. работа (20)
  6. счетчик ++
  7. запуск асинхронной функции
  8. work (30)
  9. counter ++
  10. Запуск асинхронной функции
  11. - возврат в цикл событий -
  12. Асинхронная функция завершена
  13. counter -
  14. - возврат в цикл событий -
  15. Асинхронная функция завершается
  16. counter -
  17. - возврат в цикл событий-
  18. Асинхронная функция завершается
  19. counter -
  20. Counter равен 0, поэтому вы запускаете сообщение о выполненной работе
  21. - назад в цикл обработки событий-
2 голосов
/ 28 июля 2011

Нет условий гонки. Для каждого запроса, выполняемого при уменьшении, добавлено дополнительное требование ( всегда! , включая ошибки http, которые легко забыть). Но это может быть обработано более инкапсулированным способом, оборачивая ваши звонки.

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

var ajaxWrapper = (function() {
   var id = 0, calls = {};
   return {
      makeRequest: function() {
         $.post.apply($, arguments); // for example
         calls[id] = true;
         return id++;
      },
      finishRequest: function(id) {
         delete calls[id];
      },
      isAllDone: function(){
         var prop;
         for(prop in calls) {
            if(calls.hasOwnProperty(prop)) {return false;}
         }
         return true;
      }
   };
})();

Использование:

Вместо $.post("url", ... function(){ /*success*/ } ... ); Мы сделаем

var requestId;
requestId = ajaxWrapper.makeRequest("url", ...
   function(){ /*success*/ ajaxWrapper.finishRequest(requestId); } ... );

Если вы хотите быть еще более изощренным, вы можете добавить вызовы к finishRequest внутри себя, так что использование будет почти полностью прозрачным:

 ajaxWrapper.makeRequest("url", ... function(){ /*success*/ } ... );
1 голос
/ 28 июля 2011

У меня есть служебная функция after.

var after = function _after(count, f) {
  var c = 0, results = [];
  return function _callback() {
    switch (arguments.length) {
      case 0: results.push(null); break;
      case 1: results.push(arguments[0]); break;
      default: results.push(Array.prototype.slice.call(arguments)); break;
    }
    if (++c === count) {
      f.apply(this, results);
    }
  };
};

Следующий код ниже будет работать.Поскольку javascript является однопоточным.

function doWork(work) {
  work(10);
  work(20);
  work(30);
}

WorkHandler(doWork);

function WorkHandler(cb) {
  var counter = 0,
      finish;
  cb(function _work(item) {
    counter++;
    // somethingAsync calls `finish` when it's finished
    somethingAsync(item, function _cb() {
      finish()
    });
  });
  finish = after(counter, function() {
    console.log('work finished');
  });
};

Наверное, я должен объяснить.

Мы передаем функцию, которая работает, обработчику.

Рабочий обработчик вызывает его ипереходит в работу.

Функция, которая выполняет рабочие вызовы, работает многократно увеличивая счетчик

Поскольку функция, которая работает, не является асинхронной (очень важной), мы можем определить функцию финиша после того, как онаготово.

Асинхронная работа, которая выполняется, не может завершиться (и вызвать неопределенную функцию завершения) до завершения текущего синхронного блока работы (выполнения всего обработчика).

Это означает, чточто после того, как весь обработчик завершит работу (и будет задана переменная finish), асинхронные рабочие задания начнут заканчиваться и вызовут окончания.Только после того, как все они вызовут финиш, обратный вызов отправит на after fire.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...