Функция обратного вызова динамических данных jQuery.post - PullRequest
3 голосов
/ 12 апреля 2010

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

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

В моем примере я просто использую цикл для имитации моего скрипта:

        $(document).ready(function() {
            $('a.loop').click(function() {
                $.post('loop.php', {foo:"bar"},
                function(data) {
                    $("div").html(data);                        
                });
                return false;
            });
        });

loop.php:

for ($i = 0; $i <= 100; $i++) {
    echo $i . "<br />";
}
echo "done";

Ответы [ 2 ]

5 голосов
/ 12 апреля 2010

Обновление: Получение информации о ходе выполнения намного проще, так как запросы jQuery Ajax имеют интерфейс обещаний. Используйте этот ответ:

https://stackoverflow.com/a/32272660/18771


Оригинальный ответ ниже устарел (изначально с 2010 года). Это все еще работает, но сложнее, чем должно быть. Я буду держать его на месте для справки и сравнения.


Вам нужна какая-то информация о прогрессе с сервера. Обратные вызовы ajax не выполняют прогрессивную работу, они запускаются только один раз - после успешного возврата запроса.

Итак ... в PHP вам нужно что-то вроде этого:

/* progress.php */
$batch_done = some_way_to_find_out_that_number();
$batch_size = some_way_to_find_out_that_number_too();
header('Content-type: application/json');
// TODO: format number
echo '{"progress":'. ($batch_size==0 ? '0' : $batch_done*100.0/$batch_size).'}';

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

А в JavaScript что-то вроде этого:

$(document).ready(function() {
  $('a.loop').click(function() {
    var queryData = {foo:"bar"};
    // prepare a function that does cyclic progress checking
    var progressCheck = function() {
      $.getJSON(
        "progress.php", queryData,
        function(data) { 
          $("div.progress").css("width", data.progress+"%");
        }
      )
    };
    $.post(
      'loop.php', queryData,
      /* create the post request callback now */
      function(intvalId){
        return function(data) {
          $("div").html(data);
          clearInterval(intvalId);
        }
      }( setInterval(progressCheck, 1000) )
    );
    return false;
  });
});

Эта часть требует пояснения:

function(intvalId){
  return function(data) {
    $("div").html(data);
    clearInterval(intvalId);
  };
}( setInterval(progressCheck, 1000) )

function(intvalId) - анонимная функция, которая принимает один аргумент - идентификатор интервала. Этот идентификатор необходим для остановки интервала, установленного с помощью setInterval(). К счастью, звонок на setInterval() возвращает именно этот идентификатор.

Анонимная внешняя функция возвращает внутреннюю function(data), эта будет реальным обратным вызовом для $.post().

Мы немедленно вызываем внешнюю функцию, выполняя в процессе две вещи: запуск интервала с setInterval() и передачу его возвращаемого значения (ID) в качестве аргумента. Это значение аргумента будет доступно внутренней функции во время ее вызова (что может занять несколько минут в будущем). Обратный вызов для post() теперь может фактически остановить интервал.

Как упражнение для вас;)

  • Измените вызов ajax так, чтобы он также останавливал интервал при ошибке запроса или тайм-ауте. В настоящее время, если обратный вызов никогда не запускается (и выполняется только в случае успеха!), Интервал никогда не остановится.
  • Убедитесь, что post() не может быть случайно запущен дважды.
4 голосов
/ 18 апреля 2010

Благодаря Томалаку я наконец-то собрал что-то, что работает. Поскольку при обработке пакета я не записываю свои файлы изображений на сервер, я написал файл журнала, к которому обращаюсь в сценарии progress.php.

Я хотел бы знать, если это лучший способ сделать это. Я хотел избежать записи в файл и пытался использовать PHP $ _session, но, похоже, не могу постепенно читать из него. Возможно ли это с $ _session?

HTML:

<a class="download" href="#">request download</a>
<p class="message"></p>

JS:

$('a.download').click(function() {
    var queryData = {images : ["001.jpg", "002.jpg", "003.jpg"]};

    var progressCheck = function() {
        $.get("progress.php",
            function(data) {
                $("p.message").html(data); 
            }
        );
    };

    $.post('proccess.php', queryData,
        function(intvalId) {
            return function(data) {
                $("p.message").html(data);
                clearInterval(intvalId);
            }
        } (setInterval(progressCheck, 1000))
    );

    return false;
});

process.php:

$arr = $_POST['images'];
$arr_cnt = count($arr);
$filename = "log.txt";

$i = 1;
foreach ($arr as $val) {
    $content = "processing $val ($i/$arr_cnt)";

    $handle = fopen($filename, 'w');
    fwrite($handle, $content);
    fclose($handle);

    $i++;
    sleep(3); // to mimic image processing
}

echo "<a href='#'>download zip</a>";

progress.php:

$filename = "log.txt";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);

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