最近做个人项目的时候,遇到一个问题,就是点击页面上一个按钮后,通过ajax调用php接口,ajax根据返回的数据对页面做一些改变。但同时,这个按钮点击后要存储大量的数据到数据库中。由于前端页面的变化要求快速响应,因此需要php快速返回计算结果,然后后台继续运行与数据库的交互部分代码。由于php返回给ajax数据是通过echo形式,因此第一时间想到flush函数,然而试了下却发现行不通,于是在网上找了下办法,发现大部分是针对html页面的实时交互,这类方法对ajax是无效的。如(引用网上代码):
set_time_limit ( 0 );
ob_end_clean ();
ob_start ();
$i = 10;
while ( $i > 0 )
{
$flush = $i . "<br/>";
#Microsoft Internet Explorer 只有当接受到的256个字节以后才开始显示该页面,
#所以必须发送一些额外的空格来让这些浏览器显示页面内容
$flush = str_pad ( $flush, 1000 );
echo $flush;
ob_flush ();
flush ();
sleep ( 1 );
$i --;
}
这种方式适用于直接输出结果到页面,而不能返回结果给ajax。这两种方式的区别如下:
在浏览器访问时能成功的原因是:浏览器在接收缓冲区装满时,就立即解释收到的数据,而不等待通讯的结束
在Ajax访问时不成功的原因是:XMLHttpRequest组件只负责交换数据,不负责处理数据。处理数据的代码要等到通讯结束后才执行。如果Ajax不以XMLHttpRequest 做传输载体,而以 iframe 做载体的话,上述代码是可以成功的。
后来实在没找到解决办法,可能是我搜索水平有限。。。
没办法只能去stackoverflow上求助,幸好我的英语能凑合描述清楚问题,得到了两位外国友人的帮助,成功解决。下面看下大牛提供的方法(自己补了一点中文注释):
//省略php接口中处理数据的代码
echo json_encode($res);//返回结果给ajax
// get the size of the output
$size = ob_get_length();
// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');
ob_end_flush();
ob_flush();
flush();
/******** background process starts here ********/
ignore_user_abort(true);//在关闭连接后,继续运行php脚本
/******** background process ********/
set_time_limit(0); //no time limit,不设置超时时间(根据实际情况使用)
/******** Rest of your code starts here ********/
//继续运行的代码
...
...
不得不说老外解答问题很有耐心,两个回答都写了很多注释,思路一目了然,我就不再赘述了。
PS:由于php的特性,因此在后台运行代码,依然会影响对其他请求的处理,所以对于长时间运行的代码可以考虑另起一个进程来处理,或使用多线程(swoole),或者使用消息队列(如rmq)等方式来代替。
水平有限,欢迎指正~
参考:
http://fc-lamp.blog.163.com/blog/static/174566687201271025332854/
http://bbs.csdn.net/topics/390843794
http://stackoverflow.com/questions/37182850/how-to-php-send-result-to-ajax-immediately/37183216#37183216