【问题标题】:jQuery Long Polling (With PHP server side)jQuery 长轮询(使用 PHP 服务器端)
【发布时间】:2012-07-03 01:34:46
【问题描述】:

我在这里做了一个噩梦,所以任何帮助都将不胜感激!首先,我将解释我要做什么:

我正在尝试使用 Yii 框架在我的 localhost MAMP 服务器上实现如下所述的系统:https://stackoverflow.com/a/1086448/1034392。我有一个函数可以检查数据库中是否有任何新通知 - 如果有,它会解析它们并 json 对它们进行编码。我每 5 秒在一个 while 循环中调用这个函数。

所以:转到 /user/unreadNotifications 会触发以下内容

    Yii::log('test'); // to check it's getting called  

    $this->layout=false;

    header('Content-Type: application/json');

    // LONG POLLING 
    while (Yii::app()->user->getNotifications() == null) {
        sleep(5);
    }

    echo Yii::app()->user->getNotifications(); // prints out json if new notification

    Yii::app()->end();

    return;

这很好 - 转到浏览器中的链接并验证 json 响应 - 一切都很好。

然后我尝试了各种 jQuery 的东西来让它工作......我发现工作的唯一方法是使用带有 POST 类型的 $.ajax 但只有当有等待通知时(所以返回一些 json) . $.get$.post 被“中止”(显示在 firebug 中)但 URL 被调用(因为我可以看到日志文件已更新) - 奇怪。

我使用$.get 的原始设置是:

        <script type="text/javascript">
            function notificationPoll() {
                $.get('<?php echo Yii::app()->createUrl('user/unreadNotifications') ?>','', function(result) {
                    $.each(result.events, function(events) {
                        alert('New Notification!');
                    });
                    notificationPoll();
                }, 'json');
            }
        </script>

        <script type="text/javascript">
            $(document).ready(function() {
                $.ajaxSetup({
                    timeout: 60 //set a global ajax timeout of a minute
                });
                notificationPoll();
            });
        </script>

这只是由于某种原因而“中止”。即使它不是 CORS 请求,我也尝试过使用 'jsonp'。但这也不起作用。

这似乎无济于事!有人可以参与吗?

非常感谢

【问题讨论】:

  • 忘了提及:当我使用 $.ajax 和 POST 类型并且没有要显示的通知时,任何具有 jQuery 代码的网站页面都不会加载,直到通知通过。
  • 控制台日志错误??粘贴它..

标签: php jquery ajax json getjson


【解决方案1】:

您必须确保函数在合理的时间内终止。你可以这样做:

$ttl = 10;
while ($ttl--) {
    $json = Yii::app()->user->getNotifications();
    if (null != $json) {
        break;
    }
    sleep(1);
}
if (null == $json) {
    $json = json_encode(array(
        'nothing' => true
    ));
}
header('Content-Type: application/json');
echo $json;

Yii::app()->end();
return;

您使用 setInterval() 将轮询函数设置为计时器。该函数现在将每隔 10 秒调用一次,您可能需要设置一个信号量以避免在上一次迭代返回之前调用它:

var timer = setInterval(
    function() {
        if (this.calling) {
            return;
        }
        var fn = this;
        fn.calling = true;
        $.post(url)
         .done(function(data) {
            ..
         })
         .always(function() {
            fn.calling = false;
         });
    },
    10000
);

那么 AJAX 中的轮询函数需要检查(在.done())回调中)通知是否存在:

function(data) {
    if (data.hasOwnProperty('nothing')) {
        alert('No notifications');
        return;
    }
    console.log(data);
    ...
}

现在重要的一件事是您的通知是什么样的。这里我假设它是一个 JSON 编码的 string。但如果 Yii 函数返回的是数组或对象,则需要处理其编码。这可能更干净,没有任何 IF:

header('Content-Type: ...
die(json_encode(
    array(
        'status'         => 'success',
        'notification'   => $json /* This is NULL or an array */
    )
    // Javascript side we check that data.notification is not null.
));

解码已经由jQuery处理,所以上面的变量“data”已经是一个Javascript对象,你不需要调用JSON.parse。不过,您可以检查 data 一个对象,并且它具有预期的属性。这将警告您任何错误。

要处理到另一个页面的导航,您可以将轮询Javascript函数的setInterval()提供的计时器ID存储在全局变量中,并在页面调用onUnload()以停用轮询时删除计时器。

【讨论】:

    【解决方案2】:

    如果没有通知,getNotifications 会返回什么? jQuery 期望返回 JSON 格式,但是当您只回显一个空字符串时,请求会失败,因为响应的格式不是 JSON。确保每次都回显 JSON 字符串。

    【讨论】:

    • 谢谢安迪。目前 getNotifications() 如果没有通知则返回 null ,因此 user/unreadNotifications 只是循环并且在有新通知之前实际上不会返回任何内容。
    • 它应该只是超时,没有响应。或许更好的 $.ajax 结构更容易调试,个人超时,成功,try{parse json, data_available }catch(e) { }, data_available:process JSON data, fail
    • 好的 - 看起来我终于缩小了范围!当我调用 $.ajaxSteup() 并设置超时.. 它以毫秒为单位设置超时?我已将其提高到 60000(60 秒),现在 $.get 不会“中止”!最后一个问题 - 最初加载页面很好,但是导航到另一个页面就会卡住。有没有办法在离开页面时关闭处理 GET 请求? (我猜这是导致下一页无法加载..)
    • 添加最大循环计数器或调整服务脚本的超时时间
    • @Andy 你是对的,超时时间太短了。是的,有一种方法可以中止 jQuery ajax 请求:var request = $.get(...); request.abort();
    【解决方案3】:

    这个怎么样。我假设 $.(get) 在一个名为 notificationPoll(); 的函数中。完成后将被重新调用。

    $.ajax({
        url: event_feed_href,
        async: false,
        timeout: 60000,
        done: function(data) {
                var got_json=false;
                try {
                    var json = JSON.parse(data);
                    got_json=true;
                }
                catch(e) {
                    // failed to return JSON data
                    alert('Wierd!');
                } 
                if(got_json) {
                    // process json data
                    alert('New Notification!');
                }
            },
        always: function() {
                notificationPoll();
            }
        });
    

    我已经使用 done 并且总是在这里,正如 jQuery 所说的贬低成功:失败:

    【讨论】:

    • 长轮询必须没有客户端超时。超时只能用于网络错误并重试连接。
    【解决方案4】:

    我对代码中止感兴趣,可能会有你的答案。它可能由于多种原因而发生

    • Query.get 失败。 Ajax 调用由于任何原因都不起作用。解析错误。
    • JavaScript 语法错误。例如:成功处理程序中的代码期望输入参数中的 events 属性可能不正确。

    对于 jQuery.get,按照 jQuery.get (https://api.jquery.com/jQuery.get/) 文档中的建议,该函数可以静默失败,我建议使用 .ajaxError 来查看获取是否失败。

    如果带有 jQ​​uery.get() 的请求返回错误代码,它将静默失败,除非脚本还调用了全局 .ajaxError() 方法。或者,从 jQuery 1.5 开始,jQuery.get() 返回的 jqXHR 对象的 .error() 方法也可用于错误处理。

    对于 javascript 语法错误,我会建议通过 firebug 调试器进行单步调试。在成功处理程序的第一行添加一个断点,然后从那里逐步执行每一行。这很乏味,但很有效。

    【讨论】:

      猜你喜欢
      • 2013-05-25
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-19
      相关资源
      最近更新 更多