Javascript 服务器发送的事件在脚本完成之前不会更新

Javascript 服务器发送的事件在脚本完成之前不会更新,javascript,php,server-sent-events,Javascript,Php,Server Sent Events,我有一个更新数据库的小脚本。当前处理大约需要一分钟左右,但时间会随着数据库的增加而增加。我希望在脚本运行时向用户提供更新,以便他们知道脚本的状态以及脚本仍在运行。我实现了服务器端事件,虽然它可以工作,但在脚本完成之前,所有更新都不会发布到网站上 我可以上传整个脚本文件,但这里是相关部分 首先,在脚本开始时,我检查脚本是否从命令行运行,如果不是,则发送标题,如图所示: $cli = (PHP_SAPI === 'cli') ? 1 : 0; if(!$cli){ header( 'Cont

我有一个更新数据库的小脚本。当前处理大约需要一分钟左右,但时间会随着数据库的增加而增加。我希望在脚本运行时向用户提供更新,以便他们知道脚本的状态以及脚本仍在运行。我实现了服务器端事件,虽然它可以工作,但在脚本完成之前,所有更新都不会发布到网站上

我可以上传整个脚本文件,但这里是相关部分

首先,在脚本开始时,我检查脚本是否从命令行运行,如果不是,则发送标题,如图所示:

$cli = (PHP_SAPI === 'cli') ? 1 : 0;
if(!$cli){
    header( 'Content-Type: text/event-stream' );
    header( 'Cache-Control: no-cache' );
}
这里是我的函数,用于在调用时发送sse_消息

function sse_message( $type, $message, $progress=0 )
{
    $d = array("type" => $type, "msg" => $message , "progress" => $progress);
    $id = time();

    echo "id: $id" . PHP_EOL;
    echo "data: " . json_encode($d) . PHP_EOL;
    echo PHP_EOL;

    ob_flush();
    flush();
}
最后,这里是我对侦听器的javascript实现:

if( typeof(EventSource) !== "undefined" ){
    source = new EventSource( "../priv3/script/prowip_process.php" );
    source.addEventListener("message", function(e)
    {
        var result = JSON.parse(e.data);

        switch(result.type){
            case "update":
                if( result.msg != "" )
                    document.getElementById("prowip_status").value += "\n[INFO]: "+result.msg;
                document.getElementById("prowip_progress").value = result.progress;
                break;

            case "success":
                document.getElementById("prowip_status").value += "\n[COMPLETED]:"+result.msg;
                document.getElementById("prowip_progress").value = 100;
                document.getElementById("button_prowip_exit").disabled = false;
                source.close();
                break;

            case "error":
                document.getElementById("prowip_status").value += "\n[ERROR]: "+result.msg;
                document.getElementById("button_prowip_exit").disabled = false;
                source.close();
                break;      
        }
    }, false);
}
else{
    document.getElementById("prowip_status").value += "\n\n[ERROR]: Your browser does not support server-sent events.  Please upgrade your browser or use a different browser for this function.  Please note this script will not work with Internet Explorer."
    document.getElementById("button_prowip_exit").disabled = false;
}
同样-除了在脚本过程中没有发生任何更新,然后在脚本完成所有消息和进度条更新时,所有工作都很完美

我已经在多个浏览器、计算机等上尝试过这个方法,但是在整个脚本完成之前,不会对网页进行任何更新

如有任何帮助或意见,将不胜感激。谢谢

于2015年4月3日编辑,根据要求发布更多代码

这3个函数调用sse_消息或仅调用CLI的echo

function show_error( $cli, $message )
{
    if ($cli)
        echo $message."\n";
    else
        sse_message( "error", $message );
}

function show_message( $cli, $message, $progress )
{
    if ($cli)
        echo $message."[".$progress."%]\n";
    else
        sse_message( "update", $message, $progress );
}

function show_success( $cli, $message )
{
    if ($cli)
        echo $message."[100%]\n";
    else
        sse_message( "success", $message );
}
下面是主函数的一个片段,显示了如何调用这些函数:

foreach ( $projects as $number => $project ) {
            $cn = strtok($number, "." );
            $pn = strtok(".");
            $wipover = $project['wip'] - $project['wip30'] - $project['wip60'] - $project['wip90'];
            $arover = $project['ar'] - $project['ar30'] - $project['ar60'] - $project['ar90'];
            $process_counter++;
            if( $process_counter >= $check ){
                $progress+=5;
                $check += intval ( $counter / 16 );
                show_message($cli,"",$progress);
            }
            if( ($project['empty']==1) and ($project['wip']==0) and ($project['ar']==0) ){
                //Do nothing...
            }
            else{
                $write_counter++;
                $update_query = "update projects set wip='{$project['wip']}', wip30='{$project['wip30']}', wip60='{$project['wip60']}', wip90='{$project['wip90']}', wipover='$wipover', ar='{$project['ar']}', ar30='{$project['ar30']}', ar60='{$project['ar60']}', ar90='{$project['ar90']}', arover='$arover' where client_num='$cn' and new_num='$pn'";
                if( ! $db->query( $update_query ) ){
                    show_error( $cli, "ERROR - update query: [$update_query] returned error: [".$db->error."]", $progress );
                }
            }
        }

我也有同样的问题。原来是web服务器缓存了php的输出

在PHP中,ob_flush()和flush()强制将PHP输出到web服务器,但不能强制web服务器将其推送到客户端

我的解决方案是在从PHP刷新之前将输出填充到800000个字符。i、 e:

echo "data: " . str_pad($message, 800000) . PHP_EOL . PHP_EOL;
每个回复到客户端的响应大约6Kb大,因此效率不高,但这是我让它工作的唯一方法


希望有帮助

能否显示主循环(即调用
sse_message
的PHP脚本部分)?如果您将CLI版本设置为也调用
sse_message()
,那么从命令行运行时,在分钟处理过程中它会被调用多少次?我有3个简单的函数,它们的调用取决于消息的类型,因此CLI版本不会调用sse_message,而只是使用屏幕回显。我将这些功能发布到原始消息中。然后,主循环调用这3个函数中的一个。我已经发布了一段这个过程的剪辑。如果有帮助的话,我可以发布整个函数。sse_消息现在可能被调用了大约30次。这似乎都是合理的,不是吗。如果您在浏览器的开发者控制台“网络”选项卡中查看,您是否看到接收到的字节逐渐增加。或者您是否看到接收到0个字节,然后在末尾接收所有字节?我猜是后者,缓存发生在服务器端(或中间缓存或代理服务器)。您正在使用Apache和普通的php Apache模块?谢谢Darren。脚本末尾接收到的所有字节都是正确的。我正在运行一个最近的(你解决了吗?我有相同的问题,我的事件流工作了,然后突然停止。现在数据只在脚本停止时显示。