PHP和Ajax的进度条

paw*_*elo 6 php jquery post progress request

我正在使用进度条,使用ajax请求和会话变量更新进度.当我的程序执行耗时的操作,如发送许多电子邮件等时,它只是设置适当的会话变量(包含进度值).此操作由下面的代码中的函数post()启动.

同时,第二个函数ask()每500ms循环执行一次.它应该实时返回当前进度.这就是问题:ask()发送的每个请求都在等待post()函数发送的请求完成.有趣的是,如果我设置一些像google.com这样的URL而不是url/to/progress它可以正常工作,除非它不是我想要的:).这意味着问题出在服务器端.

不确定它是否重要,但我使用的是Yii Framework.

下面的所有代码只是划痕(但有效),其唯一目的是展示我的意思.

提前致谢.

抱歉我的英语不好:)

查看部分:

<script type="text/javascript">
function ask() {
  var d = new Date();
  var time = d.getTime();
  $.ajax({

    type: 'get',
    url: '/url/to/progress' + '?time=' + time,
    success: function(data) {
      $("#progress").html(data);
    }
  })
}

function post() {
  var d = new Date();
  var time = d.getTime();

  $.ajax({
      type: 'post',
      url: '/url/to/post' + '?time=' + time,
      data: {"some": "data"},
      success: function(data) {alert(data)}
    });
}

$("#test").click(
  function() {
    post();
    var progress = setInterval("ask();", 500);
  }
);
</script>
Run Code Online (Sandbox Code Playgroud)

控制器部分:

public function actionPost($time) {
  sleep(5); // time consuming operation
  echo $time . ' : ' . microtime();
  exit;
}

public function actionProgress($time) {
  echo $time . ' : ' . microtime();
  exit;
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*dom 8

我认为你的问题与会话有关.

当脚本具有打开的会话时,它会锁定会话文件.这意味着任何使用相同会话ID的后续请求都将排队,直到第一个脚本释放它对会话文件的锁定为止.您可以强制执行此操作session_write_close()- 但这不会对您有所帮助,因为您尝试与会话文件共享进度信息,因此post脚本需要保持会话数据的开放性和可写性.

您需要提出另一种在脚本postprogress脚本之间共享数据的方法- 如果post会话数据在执行过程中打开,则在执行完毕progress之前永远无法访问会话数据post.也许您可以使用会话ID创建一个临时文件,该文件post具有写入权限,您可以在其中放置进度指示器数据.该progress可检查文件并返回数据.IPC(进程间通信)有很多选择 - 这不是一个特别漂亮的选项,但它确实具有最大的可移植性.

作为旁注 - 请不要传递字符串setInterval(),传递函数.所以你的行实际上应该是:

var progress = setInterval(ask, 500);
Run Code Online (Sandbox Code Playgroud)

但是 - 最好在ajax函数setTimeout()success/ errorhandlers中使用ask().这是因为通过使用setInterval(),无论前一个请求的状态如何,都将启动新请求.在开始下一个请求之前等待上一个请求完成会更有效.所以我会做更像这样的事情:

<script type="text/javascript">

  // We'll set this to true when the initail POST request is complete, so we
  // can easily know when to stop polling the server for progress updates
  var postComplete = false;

  var ask = function() {

    var time = new Date().getTime();

    $.ajax({

      type: 'get',
      url: '/url/to/progress' + '?time=' + time,

      success: function(data) {
        $("#progress").html(data);
        if (!postComplete)
          setTimeout(ask, 500);
        }
      },
      error: function() {
        // We need an error handler as well, to ensure another attempt gets scheduled
        if (!postComplete)
          setTimeout(ask, 500);
        }
      }

    });

  }

  $("#test").click(function() {

    // Since you only ever call post() once, you don't need a seperate function.
    // You can just put all the post() code here.

    var time = new Date().getTime();

    $.ajax({

      type: 'post',
      url: '/url/to/post' + '?time=' + time,
      data: {
        "some": "data"
      },

      success: function(data) {
        postComplete = true;
        alert(data);
      }
      error: function() {
        postComplete = true;
      }

    });

    if (!postComplete)
      setTimeout(ask, 500);
    }

  });

</script>
Run Code Online (Sandbox Code Playgroud)

...虽然这仍然无法解决会话问题.


Jon*_*Jon 5

上面的@DaveRandom正确地指出你是会话存储锁的受害者.

解决方法非常简单.您希望使进程的脚本post()释放对会话数据的锁定,以便处理的脚本ask()可以访问此会话数据.你可以这样做session_write_close.

这里的细则是,在调用之后,session_write_close您将无法访问会话变量,因此您需要相应地构造脚本post:

  1. 阅读您需要的所有数据$_SESSION并保存其副本.
  2. 调用session_write_close以释放会话锁定.
  3. 继续你的冗长操作.如果您需要会话数据,请从您自己的副本中获取,而不是$_SESSION直接获取.

或者,您可以在脚本的生命周期内多次切换会话锁定:

session_start();
$_SESSION['name'] = 'Jon';

// Quick operation that requires session data
echo 'Hello '.$_SESSION['name'];

//  Release session lock
session_write_close();

// Long operation that does not require session data.
sleep(10);

// Need access to session again
session_start();
echo 'Hello again '.$_SESSION['name'];
Run Code Online (Sandbox Code Playgroud)

这种安排使得当它睡眠的脚本时,其他脚本可以毫无问题地访问会话数据.