Зависание происходит не во всех браузерах. Например FF и Opera нормально с этим справляются. Chrome — зависает.

Итак, о чем же вообще идет речь?

Например имеем цикл:

for (var i in data) {
  $.ajax({
    url: my_url,
    async: false,
    success: function(data) {
      progress+=data;
    }
  });
}

Он не отработает как ожидается (переменная progress постепенно вырастет в зависимости от полученной переменной data) в Хроме. Вместо этого все AJAX запросы запустятся практически одновременно, из-за чего вы не заметите плавного увеличения переменной progress. И браузер перейдет в состояние «Не отвечает» пока все запросы не завершатся.

Не очень хорошее поведение, но все можно исправить.

Решение

Для решения проблемы можно написать рекурсивную функцию:

function do_progress( i, data ) {
  $.ajax({
    url: my_url,
    // async нужно установить в true
    async: true,
    success: function(response) {
      // Увеличиваем progress bar
      progress+=response;
      // Проверяем если ли у нас еще данные в объекте (не закончился ли счетчик) и увеличиваем i
      if ( typeof data[++i] != 'undefined' ) {
        // делаем тоже самое только с увеличенным i
        do_progress( i, data );
      } else {
        // заканчиваем
        alert('Finished');
      }
    }
  });
}

Таким образом, вызвав функцию do_progress( 0, data ); решится проблема с зависанием. Каждые следующий запрос будет отправлен только после завершения предыдущего и только если процесс не закончен.

Пример использовался в реальном проекте.