【问题标题】:Causing two things to load in parallel?导致两个东西并行加载?
【发布时间】:2011-06-03 18:42:04
【问题描述】:

我正在编写一些 PHP,它会进行大量处理,然后生成结果报告。以前它会定期执行 flush(),但我们正在迁移到 Zend Framework 并且不能再这样做了。相反,我希望在生成报告时更新某种状态显示。所以我做了一个进度条,在 iframe 中加载,将共享内存添加到进度条更新操作和报告生成操作,并通过 xmlhttprequest 加载输出。这一切都很好。我的问题是浏览器想要串行而不是并行执行这两个请求,因此它将请求进度条,然后阻塞,直到进度条完成,然后再请求实际输出。这意味着这个过程永远不会结束,因为真正的工作永远不会开始。

我整个上午都在寻找解决这个问题的方法,结果空手而归。 有什么方法可以导致两个连接,还是我只是搞砸了?

我的下一个动作是将处理进一步分解,让状态更新动作完成实际工作,保存结果,然后使用其他动作转储它。这会很痛苦,我想避免它。

编辑:这是 javascript,根据要求:

    function startProgress()
    {
        var iFrame = document.createElement('iframe');
        document.getElementsByTagName('body')[0].appendChild(iFrame);
        iFrame.id = 'progressframe';            
        iFrame.src = '/report/progress';
    }

    function Zend_ProgressBar_Update(data)
    {
        document.getElementById('pg-percent').style.width = data.percent + '%';
        document.getElementById('pg-text-1').innerHTML = data.text;
        document.getElementById('pg-text-2').innerHTML = data.text;
    }

    function Zend_ProgressBar_Finish()
    {
        document.getElementById('pg-percent').style.width = '100%';
        document.getElementById('pg-text-1').innerHTML = 'Report Completed';
        document.getElementById('pg-text-2').innerHTML = 'Report Completed';
        document.getElementById('progressbar').style.display = 'none'; // Hide it
    }

    function ajaxTimeout(){
        xmlhttp.abort();
        alert('Request timed out');
    }

    var xmlhttp;
    var xmlhttpTimeout;

    function loadResults(){
        if (window.XMLHttpRequest){
            // code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp=new XMLHttpRequest();
        }else{
            // code for IE6, IE5
            xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");
        }
        xmlhttp.open(\"POST\",\"/report/output\",true);
        xmlhttp.onreadystatechange=function(){
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                clearTimeout(xmlhttpTimeout);
                document.getElementById('report-output').innerHTML=xmlhttp.responseText;
            }
        }
        var xmlhttpTimeout=setTimeout(ajaxTimeout,600000); // Ten minutes
        xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        xmlhttp.send('".file_get_contents("php://input")."');           
    }

这从以下 onload 脚本中调用:

onload="startProgress(); setTimeout(loadResults,1000);"

问题不在于 Javascript。如果你在其中放置一个 alert(),警报将在正确的时间触发,但浏览器会延迟第二个 http 事务,直到第一个完成。


感谢大家的意见。

在我们的开发计划允许的时间范围内,我没有为此提出令人满意的答案。似乎每个常见的浏览器都希望在与该站点进行多项交易时重新使用与该站点的现有连接。我想不出的任何东西都不会导致浏览器按需启动并行连接。每当有来自同一服务器的两个请求时,客户端都希望以串行方式执行它们。

我最终将处理分成几部分并将其移动到状态栏更新操作中,将报告输出保存到服务器上的临时文件中,然后使状态栏完成函数启动 xmlhttprequest 以加载结果。输出操作只是吐出临时文件的内容,然后将其删除。

【问题讨论】:

  • 你能发布你正在使用的javascript吗?
  • 你为什么使用 iframe 来显示进度条。为什么不直接用 XMLHttpRequest 来实现呢?这些函数是如何被调用的?
  • 我试图使它成为一个 xmlhttprequest,但是浏览器没有解释返回的 javascript。我将它恢复为 iframe,因为它起作用了。我可以稍后尝试使其与 xmlhttprequest 一起使用,假设这甚至可行。这些函数是通过页面 body 标记的 onload 调用的。
  • 为什么要返回 JavaScript? :/ 我看不出 iFrame 无法加载的原因。
  • iframe 确实加载了。问题是 xmlhttprequest 在 iframe 完全加载之前被阻止。由于 iframe 是 xmlhttprequest 内容生成的进度指示,因此它永远不会完成。 iframe 中的 javascript 是移动进度指示器的命令。请参阅 Zend_ProgressBar 的手册以了解该机制的说明。

标签: php javascript zend-framework


【解决方案1】:

使用两个异步 ajax 可以解决问题。对于第一个 ajax 请求,您应该通过调用 php-cli 在后台深处执行实际工作来启动进程(因此它不会过期或取消)并返回进程(任务)的 id。现在,当您拥有进程 ID 后,您可以启动定期 ajax 以显示所创建的进程。

制作一个包含 process_id、state、user 的 db 表不会是一件坏事。在这种情况下,即使用户在进程运行时关闭浏览器,进程也会继续进行直到完成。用户可以重新访问该页面并查看完成的百分比,因为在 cli 中运行的进程会将进度保存到 db 表中。

【讨论】:

  • 我试图这样做,但似乎 Zend Server 无法创建后台进程,因为 fork() 不能用作 Apache 模块,而 Zend Server(至少 CE)没有t 提供 CGI 模式。
  • 我也推荐 Anze 建议的方法。你能更具体地说明你尝试了什么吗?在大多数情况下,您至少可以使用 PHP 内置调用 system() 并将输出重定向到 /dev/null 以“分离”后台进程 - 这避免了一些与操作系统相关的“双分叉”复杂性。
【解决方案2】:

对php文件进行系统调用并分离它?

例如:

exec('nohup php test.php > test.out 2> test.err < /dev/null &');

echo 'I am totally printing here';

test.php 包含 2 秒的睡眠并打印,但 echo 立即返回。

让它将结果存储在文件/数据库/任何东西中。它会像一个非常脏的叉子。

如果您在执行时遇到问题,我敢打赌,您也可以使用 CURL 调用执行类似的操作。

来自 bmellink 的代码示例归功于 here(我的比他的差得多)。

【讨论】:

    【解决方案3】:

    如果您能够在 iFrame 中加载报告,您可以反转您的逻辑(我这样做是为了跟踪文件上传到 PHP)。

    1. 在 iFrame 中加载报告(可以隐藏或您喜欢的任何内容)。
    2. 进行 ajax 调用以获取进度(第 1 步必须记录进度,正如其他人提到的那样)。
    3. 当进度报告加载完成时,您可以显示 iframe 或完成所需的任何内容。

    希望对您有所帮助。刚刚在 iFrame、CORS 和 Ajax 调用 API 方面做了很多工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-21
      • 1970-01-01
      相关资源
      最近更新 更多