Прогресс-бар с PHP и AJAX с
Я работаю над индикатором выполнения, который обновляет прогресс, используя ajax-запросы и переменные сеанса. Когда моя программа выполняет трудоемкую операцию, такую как отправка многих писем и т. д. он просто устанавливает правильную переменную сеанса (которая содержит значение прогресса). Эта операция запускается функцией post () в коде ниже.
тем временем, вторая функция ask() выполняется в цикле каждые 500 мс. Он должен вернуть текущий прогресс в реальном времени. И вот в чем проблема: каждый запрос, отправленный ask (), ждет запроса, отправленного post () функция завершена. Самое смешное, что если я установлю какой-то URL, как google.com вместо url / to / progress он работает просто отлично, за исключением того, что это не то, что я хочу :). Это означает, что проблема на стороне сервера.
не уверен, что это важно, но я использую Yii Framework.
весь код ниже-это только царапина (но работает), и его единственная цель-показать, что я имел в виду.
спасибо продвижение.
извините за мой плохой английский :)
посмотреть в составе:
<script type="text/javascript">
function ask() {
var d = new Date();
var time = d.getTime();
$.ajax({
type: 'get',
url: '/url/to/progress' + '?time=' + time,
success: function(data) {
$("#progress").html(data);
}
})
}
function post() {
var d = new Date();
var time = d.getTime();
$.ajax({
type: 'post',
url: '/url/to/post' + '?time=' + time,
data: {"some": "data"},
success: function(data) {alert(data)}
});
}
$("#test").click(
function() {
post();
var progress = setInterval("ask();", 500);
}
);
</script>
контроллер входит:
public function actionPost($time) {
sleep(5); // time consuming operation
echo $time . ' : ' . microtime();
exit;
}
public function actionProgress($time) {
echo $time . ' : ' . microtime();
exit;
}
2 ответов
я думаю, что ваша проблема здесь связана с сессией.
когда сценарий имеет открытый сеанс, он имеет блокировку файла сеанса. Это означает, что все последующие запросы, использующие один и тот же идентификатор сеанса, будут помещены в очередь до тех пор, пока первый скрипт не освободит блокировку файла сеанса. Вы можете заставить это с session_write_close()
- но это не поможет вам здесь, так как вы пытаетесь поделиться информацией о ходе работы с файлом сеанса so post
скрипт должен будет сохранить данные сеанса открыты и доступны для записи.
вам нужно будет придумать другой способ обмена данными между post
и progress
скрипты - если post
имеет данные сеанса открыты на протяжении всего его выполнения,progress
никогда не сможет получить доступ к данным сеанса доpost
закончил работу. Возможно, вы можете использовать идентификатор сеанса для создания временного файла, который post
имеет доступ для записи, в который вы помещаете данные индикатора прогресса. The progress
может проверить файл и вернуть эти данные. Есть много вариантов для IPC (межпроцессная связь) - это не особенно красивый, но он имеет преимущество максимальной переносимости.
в качестве примечания-пожалуйста, не передавайте строки в setInterval()
функции-пасс. Таким образом, ваша строка должна на самом деле читать:
var progress = setInterval(ask, 500);
но-было бы лучше использовать setTimeout()
на success
/error
обработки ask()
функция ajax. Это потому, что с помощью setInterval()
новый запрос будет быть возбуждено независимо от состояния предыдущего. Было бы более эффективно дождаться завершения предыдущего запроса, прежде чем инициировать следующий. Поэтому я бы сделал что-то еще вроде этого:
<script type="text/javascript">
// We'll set this to true when the initail POST request is complete, so we
// can easily know when to stop polling the server for progress updates
var postComplete = false;
var ask = function() {
var time = new Date().getTime();
$.ajax({
type: 'get',
url: '/url/to/progress' + '?time=' + time,
success: function(data) {
$("#progress").html(data);
if (!postComplete)
setTimeout(ask, 500);
}
},
error: function() {
// We need an error handler as well, to ensure another attempt gets scheduled
if (!postComplete)
setTimeout(ask, 500);
}
}
});
}
$("#test").click(function() {
// Since you only ever call post() once, you don't need a seperate function.
// You can just put all the post() code here.
var time = new Date().getTime();
$.ajax({
type: 'post',
url: '/url/to/post' + '?time=' + time,
data: {
"some": "data"
},
success: function(data) {
postComplete = true;
alert(data);
}
error: function() {
postComplete = true;
}
});
if (!postComplete)
setTimeout(ask, 500);
}
});
</script>
...хотя это все еще не устраняет проблему сеанса.
@DaveRandom выше правильно указывает, что вы являетесь жертвой блокировки хранения сеанса.
обходной путь довольно прост. Вы хотите сделать скрипт, который обрабатывает post()
отпустите блокировку данных сеанса, чтобы скрипт, который обрабатывает ask()
доступ к данным сеанса. Вы можете сделать это с session_write_close
.
мелким шрифтом здесь является то, что после вызова session_write_close
у вас не будет доступа к переменным сеанса, поэтому вам нужно структурировать сценарий для post
таким образом:
- прочитайте все данные, которые вам понадобятся от
$_SESSION
и сохраните его копию. - вызов
session_write_close
чтобы освободить блокировку сеанса. - продолжайте свою длительную работу. Если вам нужны данные сеанса, получите их из своей копии, а не
$_SESSION
напрямую.
кроме того, вы можете переключать блокировку сеанса несколько раз в течение жизни скрипта:
session_start();
$_SESSION['name'] = 'Jon';
// Quick operation that requires session data
echo 'Hello '.$_SESSION['name'];
// Release session lock
session_write_close();
// Long operation that does not require session data.
sleep(10);
// Need access to session again
session_start();
echo 'Hello again '.$_SESSION['name'];
этот механизм делает это так, что пока скрипт спит, другие скрипты могут получить доступ к данным сеанса без проблем.