PHP to get long running process progress dynamically

English 简体中文 繁体中文 ภาษาไทย Tiếng Việt
Summary

Web applications frequently face challenges in providing dynamic progress updates for long-running backend processes, often leading to a poor user experience due to HTTP's stateless nature and the absence of multithreading in server-side scripting. A practical solution involves simulating real-time progress using PHP and jQuery AJAX. This approach utilizes a backend PHP script to execute the long process, periodically updating a session variable to store its progress, critically employing `session_write_close()` to release the session lock. Concurrently, a frontend script initiates the long process and then repeatedly polls a separate PHP endpoint via AJAX to fetch and display the current progress, keeping users informed. This method effectively enhances user experience by dynamically showing the status of extended operations.

在Web应用程序中,我们经常会向后端系统发出请求,这可能会触发一个长时间运行的进程,例如搜索大量数据或长时间运行的数据库进程。然后前端网页可能会挂起并等待进程完成。在此过程中,如果我们可以向用户提供一些关于后端进程进度的信息,则可以改善用户体验。不幸的是,在Web应用程序中,这似乎不是一项容易的任务,因为Web脚本语言不支持多线程,并且HTTP是无状态的。现在我们可以使用AJAX来模拟实时进程。今天我们将使用PHP+jQuery来模拟一个动态获取长时间运行进程进度的过程。

我们需要三个文件:

<?php
    //long_process.php
    for($i=1;$i<=10;$i++){
        session_start();
        $_SESSION["progress"]=$i;
        session_write_close();
        sleep(1);
    }
?>


上面的代码是long_process.php,它模拟长时间运行的进程,它实际上有一个for循环,该循环将从1循环到10,并在每次循环后休眠一秒钟。这里最重要的事情是我们使用会话变量$_SESSION["progress"]来存储进度,也不要忘记在每次循环后使用session_write_close(),因为会话数据通常在您的脚本终止后存储,而无需调用session_write_close(),但是由于会话数据被锁定以防止并发写入,因此任何时候只有一个脚本可以操作会话。当将框架集与会话一起使用时,由于这种锁定,您将体验到框架一个接一个地加载。您可以通过在对会话变量的所有更改完成后结束会话来减少加载所有框架所需的时间。

还有一个文件progress.php

<?php
    //progress.php
    session_start();
    echo $_SESSION["progress"];
?>

它所做的非常简单,只是将进度回显给客户端。此进度变量是在long_process.php中更新的会话变量。

最后一个PHP文件:

<script type="text/javascript">
    //启动长时间运行的进程
    $.ajax({
        url: 'long_process.php',
        success: function(data) {
        }
    });
    //开始接收进度
    function getProgress(){
        $.ajax({
            url: 'progress.php',
            success: function(data) {
                $("#progress").html(data);
                if(data<10){
                    getProgress();
                }
            }
        });
    }
    getProgress();
</script>
<div id="progress"></div>

上面的代码只包含内部代码(我省略了其余代码),它实际上是模拟的入口,它使用AJAX向服务器发送两个请求,一个用于初始化长时间运行的进程,另一个用于轮询服务器以获取进度。获取进度后,它会在页面上更新进度。

当然,还有其他方法可以存储进度变量,例如数据库或xml文件等。您可以选择您喜欢的方法或您认为最有效的方法。此外,除了纯PHP代码之外,您还可以使用Flash或Java小程序通过许多网站使用的TCP或UDP来处理和轮询长时间运行的进程。

如果您有比这更好的方法,可以与我们分享。

PHP AJAX DEMO PROGRESS LONG PROCESS

  RELATED

  COMMENTS

10
Yongd
Apr 17, 2014 at 9:27 am

Thank you,this's very helpful to me!

Pi Ke
Apr 17, 2014 at 9:54 am

It's my pleasure

Alexander Avakov
Feb 17, 2015 at 6:03 am

Nice tutorial. Thank you!

alea123
May 7, 2015 at 2:22 am

rly nice.. thank you!

samas
Jun 23, 2015 at 2:14 am

nice article, care to put up a complete working demo... full source code which can be easily adapted...?

verjas
Aug 21, 2015 at 2:51 am

I've just tested it on a project.

Works like a charm! Good job! 

Patrick
Nov 25, 2015 at 8:40 am

Very nice!!! Saved me hours!

Maksim Pol
Jan 10, 2016 at 2:55 am

It sometimes starts wrong. After page refresh, progress percent stays, but script working in background. If i make refresh again, then will run 2 scripts at one time. 

Anonymous
Jul 19, 2020 at 4:04 am

I had this similar issue.

I solved it by;

1. If() statement to prevent the long running script starting when the session variables already exist.

2. Unset() the session variables when the long script is complete and the last Ajax request has been called.

Maksim Pol
Jan 10, 2016 at 2:55 am

How it can be fixed? Anyway, thanks for the tutorial. :)