8

考虑一个写入 div 的 AJAX 调用:

recent_req=$.post('result.php', { d: data }, function(returnData) {
    $('#content').html(returnData);
});

PHP 脚本result.php执行一些需要时间的功能,每步大约需要 5-20 秒。我正在使用 PHP 的flush()函数在每个步骤开始和结束后立即将信息发送到浏览器,但是如何让 Javascript 在数据进入时将数据写入#contentdiv?

谢谢。

编辑: 澄清:假设result.php如下所示,由于限制,实际上无法重构:

<?php

echo "Starting...<br />";
flush();

longOperation();
echo "Done with first long operation.<br />";
flush();

anotherLongOperation();
echo "Done with another long operation.<br />";
flush();

?>

如何构建 AJAX 来调用result.php,使得 echo 语句在进入时附加到#contentdiv 中?欢迎任何带/不带 jQuery 的解决方案。谢谢!

4

7 回答 7

5

有一种使用 iframe 的技术,您可以使用它来实现这一点。

类似于其他涉及框架的建议,但它不涉及会话或轮询或任何东西,并且不需要您显示 iframe 本身。它还具有在过程中的任何时候运行您想要的任何代码的好处,以防您使用 UI 做一些比将文本推送到 div 更复杂的事情(例如,您可以更新进度条)。

基本上,将表单提交到隐藏的 iFrame,然后将 javascript 刷新到该框架,该框架与 iFrame 父级中的函数进行交互。

像这样:

HTML:

<form target="results" action="result.php" method="post">
<!-- your form -->
<input type="submit" value="Go" />
</form>

<iframe name="results" id="results" width="0" height="0" />
<div id="progress"></div>

Javascript,在您的主页中:

function updateProgress(progress) {
  $("#progress").append("<div>" + progress + "</div>");
}

结果.php:

<?php

echo "<script language='javascript'>parent.updateProgress('Starting...');</script>";
flush();

longOperation();
echo "<script language='javascript'>parent.updateProgress('Done with first long operation.');</script>";
flush();

anotherLongOperation();
echo "<script language='javascript'>parent.updateProgress('Done with another long operation.');</script>";
flush();

?>
于 2012-11-20T15:55:31.807 回答
4

您无法使用常规 ajax 调用“流式传输”数据,因为您无法让用户的浏览器“监听”服务器请求。只有在数据处理完成后才会调用您的“成功”函数。

不过,互联网上有很多关于“Ajax Push”的讨论,显然 HTML5 具有 websocket 对象,可用于让用户的浏览器监听服务器请求。语法定义还不是很稳定,所以你不想弄乱它,因为它可能很快就会改变。

您可能想要做的是为 step1 发送一个请求,等待它的返回,然后为 step2 发送一个请求。它会为您的整体处理时间增加一些开销(并使其更加冗长),但如果您只有几个大步骤,它应该可以正常工作。如果您的步骤不需要太多处理,则不应该这样做(因为通信时间将变得大于您的“有效处理时间”)。

编辑:例如,您还可以在用户会话中写入进度。这样,您可以定期 ping 服务器并请求更新状态。这样,即使您有许多小步骤,您也只需每 10 秒左右分派一次请求,这是对每一步分派的改进。

于 2012-11-12T14:06:03.067 回答
3

作为替代解决方案,您可以将隐藏提交formiframe中,如以下示例所示:

<?php

function output_data($data)  {
    echo str_pad($data, 4096, ' ', STR_PAD_RIGHT) . "\n";
    flush(); 
}

function long_runner() {        
    output_data("");

    output_data(date("H:i:s").' - Starting...<br />');
    sleep(10);

    output_data(date("H:i:s").' - Done with first long operation.<br />');
    sleep(10);

    output_data(date("H:i:s").' - Done with another long operation.<br />');
    return("<script>parent.task_complete()</script>");
}

if (isset($_REQUEST["status"])) {
    die(long_runner());
}

?>
<!DOCTYPE html>
<html>
    <head>
        <title>Write to IFRAME as data streams in</title>
        <style>
        #myform { display: none }
        #frm { width: 50% }
        </style>
        <script language="javascript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
        <script>
        function task_complete() {
            alert('Task completed');
        }

        $(document).ready(function() { 
            $('#starter').click(function() {
                $('#myform').submit();
            });
        });
        </script>
    </head>
    <body>
        <form id="myform" method="get" target="frm" action="<?= $_SERVER['SCRIPT_NAME'] ?>">
            <input type="hidden" name="status" value="0">
        </form>
        <a href="#" id="starter">Start</a><br />
        <iframe id="frm" name="frm" frameborder="0"></iframe>
    </body>
</html>
于 2012-11-14T22:12:16.923 回答
3

将动态数据流写入 div:

在这里..您专门询问了如何将数据流动态写入“div”。正如许多人所说,可以动态写入 iframe,我们只需要更进一步。这是您问题的完整解决方案,它将以 0.5 秒的最大延迟将该数据带回您的 div。如果您需要更及时的更新,可以对其进行调整。

<!DOCTYPE html>
<html>
<head>
    <title>dynamic listener</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script type="text/javascript">
    var count;
    $(function(){
        $('#formx').submit(function(){
            setTimeout(function(){ check_div(); }, 500);
            count = 0;
            return true;
        });
    });

    function check_div()
    {
        var $iframetxt = $('#iframex').contents().text();
        var $div = $('#dynamic');
        if( $iframetxt != $div.text() )
        {
            console.log('rewritten!');
            $div.text( $iframetxt );
            setTimeout(function(){ check_div(); }, 500);
            count = 0;
        }
        else
        {
            count++;
            if(count < 40) setTimeout(function(){ check_div(); }, 500);
            else console.log('timed out');
        }       
    }
    </script>
</head>
<body>
    <div>
        Form
        <form id="formx" action="result.php" method="post" target="iframex">
            <input type="submit" />
        </form>
    </div>
    <div id="dynamic"></div>
    <iframe id='iframex' name="iframex" style="display:none" ></iframe>
</body>
</html>

1. 在表单提交时,流数据被发送到 iframe。

为此,我们只需将表单标签中的目标属性设置为 iframe 名称。

2. check_div() 每 0.5 秒运行一次,将#dynamic div 的文本与 iframe 的文本内容进行比较。

如果它们之间存在差异,则将数据写入div并再次调用超时。如果没有差异,则超时计数器递增。如果计数小于 40(40 x .5 秒 = 20 秒),它会再次调用超时。如果没有,我们假设流已经完成。

于 2012-11-16T21:21:39.833 回答
2

这是使用会话轮询的解决方案:

HTML:

<!DOCTYPE html>
<html>
<body>
    <div id="content"></div>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script>
    var pollTimeout;
    function pollResult(){
        $.get('poll.php', function(response) {
            // Update #content with partial response
            $('#content').html(response);
            pollTimeout = setTimeout(pollResult, 1000);
        });
    }
    $.post('result.php', function(response) {
        // Result is loaded, stop polling and update content with final response
        clearTimeout(pollTimeout);
        $('#content').html(response);
    });
    // Start polling
    pollResult();
    </script>
</body>

</html>

结果PHP:

<?php

class SemiStream{

    public function __construct(){
        @session_start();
        $_SESSION['semi_stream'] = '';
    }

    public function write($data){
        @session_start();
        $_SESSION['semi_stream'] .= $data;
        // We have to save and close the session to be
        // able to read the contents of it in poll.php
        session_write_close();
    }

    public function close(){
        echo $_SESSION['semi_stream'];
        unset($_SESSION['semi_stream']);
    }
}

$stream = new SemiStream();
$stream->write("Starting...<br />");

sleep(3);

$stream->write("Done with first long operation.<br />");

sleep(3);

$stream->write("Done with another long operation.<br />");
$stream->close();

echo 'Done.';

投票PHP:

<?php
session_start();
echo $_SESSION['semi_stream'];

这工作,无需使用 PHP 的输出缓冲。

于 2012-11-14T23:46:43.080 回答
0

查看 Pusher 服务,它似乎可以完全满足您的需求: http: //pusher.com/

于 2012-11-15T20:34:04.820 回答
0

可能,问题是关于如何在您的应用程序中实现推送技术。我建议你看一下这个问题,它有很好的例子

于 2012-11-19T13:07:04.540 回答