我有一个Ajax调用,可以更新数据库中的5,000条记录,所以这需要很多时间。我有一个Ajax的“加载图像”显示正在发生的事情,但我正在寻找一种更好的方式来显示“更新5000个5000 ...”,“更新5000个5000”或类似的东西。在Ajax调用中显示进度的最佳方式是什么?
在没有5000个不同的帖子的情况下,在Ajax/jQuery中做这样的事情的最佳方式是什么?
我有一个Ajax调用,可以更新数据库中的5,000条记录,所以这需要很多时间。我有一个Ajax的“加载图像”显示正在发生的事情,但我正在寻找一种更好的方式来显示“更新5000个5000 ...”,“更新5000个5000”或类似的东西。在Ajax调用中显示进度的最佳方式是什么?
在没有5000个不同的帖子的情况下,在Ajax/jQuery中做这样的事情的最佳方式是什么?
我认为最好的是使用Comet。
在Comet风格的应用程序中,服务器本质上可以将数据推送到客户端(而不是一次又一次地从服务器端发送客户端请求数据)。客户端需要连接到服务器一次。然后服务器会继续将数据推送回客户端。
维基百科:
Comet是一种编程技术,使网络服务器将数据发送到客户端,而不需要任何客户端请求它。它允许创建托管在浏览器中的事件驱动的Web应用程序。
现在我们来看看Comet如何工作。请参阅以下服务器端代码。这里使用了一个while
循环,您可以改为设置自己的条件。在while循环中,页面写入日期时间并刷新,然后休眠1/2秒。
ASP.NET页面的代码隐藏:Service.aspx.cs
public static string Delimiter = "|";
protected void Page_Load(object sender, EventArgs e)
{
Response.Buffer = false;
while (true)
{
Response.Write(Delimiter
+ DateTime.Now.ToString("HH:mm:ss.FFF"));
Response.Flush();
// Suspend the thread for 1/2 a second
System.Threading.Thread.Sleep(500);
}
// Yes I know we'll never get here,
// it's just hard not to include it!
Response.End();
}
客户端代码 - 纯JavaScript
只有发出请求一次,然后继续检查数据在XMLHttpRequest
的readyState === 3
中。
function getData()
{
loadXMLDoc("Service.aspx");
}
var req = false;
function createRequest() {
req = new XMLHttpRequest(); // http://msdn.microsoft.com/en-us/library/ms535874%28v=vs.85%29.aspx
}
function loadXMLDoc(url) {
try {
if (req) { req.abort(); req = false; }
createRequest();
if (req) {
req.onreadystatechange = processReqChange;
req.open("GET", url, true);
req.send("");
}
else { alert('unable to create request'); }
}
catch (e) { alert(e.message); }
}
function processReqChange() {
if (req.readyState == 3) {
try {
ProcessInput(req.responseText);
// At some (artibrary) length recycle the connection
if (req.responseText.length > 3000) { lastDelimiterPosition = -1; getData(); }
}
catch (e) { alert(e.message); }
}
}
var lastDelimiterPosition = -1;
function ProcessInput(input) {
// Make a copy of the input
var text = input;
// Search for the last instance of the delimiter
var nextDelimiter = text.indexOf('|', lastDelimiterPosition + 1);
if (nextDelimiter != -1) {
// Pull out the latest message
var timeStamp = text.substring(nextDelimiter + 1);
if (timeStamp.length > 0) {
lastDelimiterPosition = nextDelimiter;
document.getElementById('outputZone').innerHTML = timeStamp;
}
}
}
window.onload = function() { getData(); };
小心......每个浏览器都会处理这个问题的方式有所不同 - 即它不起作用。猜测浏览器的行为与其他浏览器不同。有关更多详情,请参阅下面的答案。 – user406905 2010-12-27 19:32:30
我假设你有一个单独遍历每条记录的原因,而不是简单地运行一条SQL语句。
如果是这样的话,只需每200次左右迭代就做一次ajax调用。如果您为每组200条记录执行此操作,则它只会消耗50个Ajax调用。
类似的信息(伪):
If iterationNumber mod 200 == 0
// Make Ajax call.
我目前使用的是在批量更新中的所有记录一个POST,并把调用和返回的加载图像假设。
与其让服务器等待直到完成更新才返回,请立即返回该批次更新的特殊ID。然后实现一个服务器调用,返回批量更新的状态,您的进度对话框可以调用该报告来报告进度。
var progressCallback = function(data){
//update progress dialog with data
//if data does not indicate completion
//ajax call status function with "progressCallback" as callback
});
//ajax call status function with "progressCallback" as callback
这与Splunk的REST API(以及其他Web服务)的工作原理有些相似。 “Jobs”作为一个粗略的例子 - http://www.splunk.com/base/Documentation/latest/Developer/RESTSearch – 2010-10-10 18:29:42
http://en.wikipedia.org/wiki/Comet_%28programming%29(来自http:/ /stackoverflow.com/questions/2572830/show-progress-during-a-long-ajax-call) – 2010-10-10 21:17:02
我先给是每个单个(或很多)更新后做的大更新记录在会话变量的当前进度的功能,并使用单独的AJAX脚本从session中获取此进度值让JavaScript使用它来更新你的进度条/文本。
这听起来有点乱。 。你有任何这样做的例子? – leora 2010-12-14 04:06:05
我会每隔n发出一次Ajax回调,它可以查询完成了多少操作(例如更新的记录数)并使用它来显示进度条。类似于this的工作方式。
您可以使用进度更新响应缓冲区,定期从服务器中刷新响应缓冲区。
但是,在通过xhttpr
完成请求之前,您可能无法读取请求。您可能可以通过iframe发出请求,并通过“http流”进行加载。
但即使这可能是粗略的。 HTTP并不是意味着零散/零碎的东西。像其他人一样指出,最好是单独进行后续调用以获取操作状态。
为了显示加载过程中的进度,我会修改我的后端,以便它可以执行选择性加载。
例如,
var total_rec = 5000;
var load_steps = 20;
var per_load = total_rev/load_steps;
var loaded = 0;
while (loaded < total_rec) {
www.foobar.com/api.php?start=loaded&end=(loaded+per_load);
loaded += per_load;
}
每次加载完成后,更新进度条。
的另一种方式修改后端可以
www.foobar.com/api.php?start=loaded&count=50
我只是有一个想法,而读的答复。
JavaScript和PHP份额饼干,
优点:
哇可能吗?在没有多个Ajax调用的情况下,cookie的变化值如何在客户端(javascript)和服务器之间传递? – 2010-12-17 21:07:32
javascript会检查cookie值与递归读取cookie功能,它不会使用ajax。 http://www.w3schools.com/js/js_cookies.asp检查此链接以阅读使用javascript的cookie – 2010-12-20 06:36:04
在客户端和服务器之间必须存在一些内部握手以供cookie更新反映。可能我们能够改变服务器端的cookie值,并在客户端读取它,因为这种内部通信。没有这个,怎么可能? – 2010-12-20 10:12:46
东西似乎很腥。
我不熟悉无法在闪存中更新5000条记录的数据库...因此,这不仅仅是您正在应用的单个更新,还是通过记录更新创建的记录?
考虑一个系统,它允许用户下载5000个条目并且不标记哪些已被编辑,然后在更新结束时应用单个更新按钮,这将需要所有5000条记录以某种方式传回。那将是最坏的情况。
因此,可能有某种方法来分割问题,以便不会有等待时间。考虑一个临时数据库表(或者只是在应用程序内存中,通过创建一个ORM实体列表非常简单,但这只是一个问题),那么当提交这些更新时,他们至少可以在不需要任何客户端/服务器传输。甚至可以标记编辑过的各个字段,因此除了刚才发生了什么变化之外,没有更多的数据库更新。
有很长时间的运行过程,我知道有时候你被困在不得不使用别人提供给你的东西......但也许有一点想法,你可以简单地摆脱等待时间。
我不确定服务器端是在哪里发布,但您应该能够将此方法应用于大多数编程语言。我以PHP为例。
在HTML端,有一些将进度条更新为给定宽度的函数。我为这个例子调用了这个函数'setProgress',它需要一个数字来更新进度条。
在服务器端代码中,执行更新块(比如每次迭代100次)并生成输出。通过输出javascript调用每次迭代:
<?php
while() { // Whatever your loop needs to be.
// Do your chunk of updates here.
?>
<script type="text/javascript">
setProgress(100);
</script>
<?php
flush(); // Send what we have so far to the browser so the script is executed.
}
setProgress(5000); // All done!
?>
与此相呼应后,刷新输出缓冲区,以确保这个数据被发送到浏览器。因为它是一个完整的脚本标记,所以浏览器将在里面执行javascript,将进度条更新为您传递给它的任何值。
为了让整件事情起作用,您需要添加一些计算来理解调用进度条的数字,但我认为这不应该是太多的问题。在示例中使用百分比的setProgress可能更有意义,但为了清晰起见,我想避开所需的计算。
创建一个简单的表是这样的:
CREATE TABLE progress_data (
statusId int(4) NOT NULL AUTO_INCREMENT,
progress float DEFAULT NULL COMMENT 'percentage',
PRIMARY KEY (id_progress_data)
);
jQuery代码:
//this uses Jquery Timers http://plugins.jquery.com/project/timers
$('#bUpdate').click(function() {
//first obtain a unique ID of this operation - this has to by synchronized
$.ajaxSetup({'async': false});
$.post('ajax.php', {'operation': 'beginOperation'}, function(data) {
statusId = parseInt(data.statusId);
});
//now run the long-running task with the operation ID and other params as necessary
$.ajaxSetup({'async': true});
$.post('ajax.php', {'operation': 'updateSite', 'statusId': statusId, 'param': paramValue}, function(data) {
$('#progress_bar').stopTime('statusLog'); //long operation is finished - stop the timer
if (data.result) {
//operation probably successful
} else {
//operation failed
}
});
//query for progress every 4s, 'statusLog' is just the name of the timer
$('#progress_bar').everyTime('4s', 'statusLog', function() {
var elm = $(this);
$.post('ajax.php', {'operation': 'showLog', 'statusId': statusId}, function(data) {
if (data) {
//set bar percentage
$('#progress').css('width', parseInt(data.progress) + '%');
}
});
});
return false;
}
后端代码(PHP):
if (isset($_POST['operation'])) {
ini_set("display_errors", false);
session_write_close(); //otherwise requests would block each other
switch ($_POST['operation']) {
/**
* Initialize progress operation, acquire ID (statusId) of that operation and pass it back to
* JS frontend. The frontend then sends the statusId back to get current state of progress of
* a given operation.
*/
case 'beginOperation': {
$statusId = //insert into progress_data
echo json_encode(array('statusId' => $statusId));
break;
}
/**
* Return back current progress state.
*/
case 'showLog': {
$result->progress = (float) //SELECT progress FROM progress_data WHERE statusId = $_POST['statusId']
echo json_encode($result);
break;
}
case 'updateSite': {
//start long running operation, return whatever you want to, during the operation ocassionally do:
UPDATE progress_data SET progress=... WHERE statusId = $_POST['statusId']
}
}
}
/* Terminate script, since this 'view' has no template, there si nothing to display.
*/
exit;
我已经用在3个应用这种方法已经和我必须说它是非常可靠和快速的enogh(showLog操作只是一个简单的SELECT语句)。也可以使用会话来存储进度,但会带来很多问题,因为会话必须写入关闭(如果它存储在文件中),否则showLog AJAX查询将等待长时间操作完成(和松散的感觉)。
您的代码实际上是每次轮询服务器。 OP不需要轮询,正如他明确提到的*'不做5000个不同的职位'* – 2010-12-20 08:56:22
那么,如果运行4个小时,这段代码将向服务器发出5000个请求。在10分钟的操作中,这将提出150个请求,这与5000个相差很远。他没有说他希望在一个请求中使用它。 – Odin 2010-12-21 08:26:44
我曾经做过类似的东西这种方式(类似于Zain Shaikh,但更简单):
在服务器
int toUpdate = 5000;
int updated = 0;
int prev = updated;
while(updated < toUpdate)
{
updated = getAlreadyUpdatedRows();
flushToClient(generateZeroSequenceOfLength(updated - prev));
prev = updated;
sleep(500);
}
closeStream();
在客户端
按照Zain Shaikh的路径,但是ProcessInput
根据要更新的记录数与input.length
之间的比率,简单地调整进度栏的大小。
该解决方案通常针对网络带宽交易客户端复杂性。
不要将此服务器活动与最终的文件导入混合在一起,除非您确实知道自己在做什么。 你会交易数据库查询(选择计数更新行)的稳定性:如果用户更改页面?进度条没有问题,但导入不会完成。
什么是一些有趣的加载语句,以保持用户的逗乐? http://stackoverflow.com/questions/182112/what-are-some-funny-loading-statements-to-keep-users-amused – 2010-10-10 18:13:26
[在长Ajax调用期间显示进度]可能的重复(http:// stackoverflow .com/questions/2572830/show-progress-during-a-long-ajax-call) – 2010-10-10 18:38:35
@Amr - 但是那些不显示进度,只是正在加载内容。 – 2010-10-10 20:30:36