【问题标题】:jQuery polling chat - duplicate entriesjQuery 轮询聊天 - 重复条目
【发布时间】:2012-11-07 20:52:21
【问题描述】:

这是我的场景:

  • 每当用户发送新消息时,我都会将其作为预览附加到对话线程,而HTTP POST 请求会将其保存到服务器。
  • 每隔一段时间,我会使用 setInterval 检查对话中的新消息。
  • 如果返回任何新消息,我删除消息的预览版本,然后从数据库中附加任何新消息。

这是生成聊天内容的脚本:

function refresh_chat(){
    var last = $('.conversation li:not(.fake):last').data('id');
    $.post('includes/router.php', {
        task: 'update_conversation',
        id: '<?=$_GET['conversationid']?>',
        last: last
    }, function (data, response) {
        var recibidas = $(data).find('li');

        /* IF there are new entries */
        if (recibidas.length > 0) {
            /* Remove all fake entries */
            $('.conversation li.fake').remove();

            /* Append new entries */
            $('.conversation').append($(data).filter('.notifications').html());

            /* If this new entries are not unread, 
               remove the unread to the previous ones*/
            if(!$(data).find('li:last').hasClass('unread')) {
                $('.conversation li.unread').removeClass('unread');
            }
        }
    });
}

var t = setInterval(function () {   
    refresh_chat();
}, 3000);

这就是我在用户键入时添加新条目的方式:

$('body').on('submit', '.send_message_form_conversation', function(e) {
    e.preventDefault();
    var id_to = $(this).find('#id_to').val();
    var msj = $(this).find('#msj').val();
    if (msj.length >= 2) {
        $(this).find('#msj').val('');
        $(this).find('.nicEdit-main').html('');
        //alert(id_to);
        $('.conversation').append(
            '<li class="unread fake">' +
             '<div class="avatar">' +
              '<a href="index.php?userid=<?=sesion()?>">' +
               '<img alt="" src="<?=$_SESSION['avatar']?>">' +
              '</a>' +
             '</div>' +
             '<div class="txt">' +
              '<a class="userName" href="index.php?userid=<?=sesion()?>">' + 
               '<?=$_SESSION['alias']?> -- ' +
               '<span class="date">' +
                "<?=get_texto_clave('ahora mismo')?>" +
               '</span>' +
              '</a>
             '<span class="msj">' + msj + '</span>' + 
            '</div>' +
            '<span data-id="47" class="close">X</span>' + 
           '</li>');

        $.post('includes/msj.php?', {
            task  : 'post_message',
            id_to : id_to,
            msj   : msj
        }, function (data, response) {
            $(".conversation").scrollTop($(".conversation")[0].scrollHeight);
        });
    } else {
        $(this).parent().effect("shake", { times:0, distance: 3 }, 200);
    }                       
});

如您所见,&lt;li&gt; 项可能有两个类:.fake(表示该项是用户刚刚提交的内容的预览,已被 js 附加)或.unread(此表示接收方刚刚收到消息)

我一直在努力解决的问题是,有时我开始看到一些重复的条目(但仅显示 - 它们在数据库中没有重复)。我猜我的间隔有问题?

这可能是什么原因造成的? (我一直在读它,但我找不到任何奇怪的东西......)

PD:基本上,有些消息会显示不止一次:S

-编辑-

$q = "SELECT * FROM pms " .
     "WHERE ((id_to = $id and id_from = " . sesion() . ") OR " .
     "       (id_from = $id and id_to = " . sesion() . ")) " .
     "AND (id > $from) " .
     "ORDER by fecha ASC " . $limit;

此查询是 $.post() 请求中使用的查询,其中 $from 是 JavaScript 参数中的最后一个(代表显示给用户的最后一条消息)

【问题讨论】:

  • 使用轮询 ajax 请求的聊天确实不是最佳实践。
  • 您的includes/router.php?task=update_conversation 脚本如何确保不发送两次条目?
  • 它没有。因为在数据库中没有重复 @Bergi (gdoron) 所以你有什么建议?
  • 数据库中没有重复项,是的,但是您如何控制在单个请求中将哪些条目发送给用户?你不发送整个数据库,是吗?你应该只向用户发送那些新的......
  • @Bergi 这就是我正在做的(检查我的编辑)

标签: javascript jquery setinterval


【解决方案1】:

想一想场景:

  1. refresh_chat() - 请求发送到服务器
  2. 3 秒过去了
  3. refresh_chat() - 发送到服务器的相同请求
  4. 对第一次收到的响应。添加了新条目。
  5. 收到第二个响应。再次添加了相同的条目。

解决这个问题最简单的方法是去掉setInterval,在处理完响应后加上setTimeout。

【讨论】:

    【解决方案2】:

    试试 Socket.io!即使您将消息发送到服务器,在那里对其进行解析和转换并将其发送回,也不会有任何明显的延迟,因此无需进行预览。

    在聊天应用程序中使用 Ajax 和 setTimeout 只会给您带来痛苦。

    【讨论】:

    • 任何适用于我的问题的例子?
    【解决方案3】:

    当问题出现时,检查您从服务器返回的数据(即 ajax 响应正文)是否也存在“旧”数据。如果是这种情况,您必须调试您的查询。

    另外,您的数据库查询很容易受到 SQL 注入攻击 http://en.wikipedia.org/wiki/SQL_injection (例如提供 last 的值为 : x'; DROP TABLE pms; -- )

     $q = "SELECT * FROM pms " .
     "WHERE ((id_to = $id and id_from = " . sesion() . ") OR " .
     "       (id_from = $id and id_to = " . sesion() . ")) " .
     "AND (id > $from) " .
     "ORDER by fecha ASC " . $limit;
    

    最后,投票似乎并不是实现聊天应用程序的最佳方式。 也许套接字和轮询(如果浏览器不支持套接字)的组合会更好。 最好的 C

    【讨论】:

      【解决方案4】:

      我认为这是因为如果要显示新条目,您只是在“隐藏”假条目:

      if(recibidas.length>0){
          $('.conversation li.fake').remove();
      

      我建议将其从 if 语句中删除。似乎您应该在发布新消息后拥有remove(),例如在提交功能的function(data,response){ 中。

      【讨论】:

      • 我不这么认为。我实际上是在删除假元素(不是隐藏!!!我看不出你从哪里得到的)。但我应该因为如果 recibidas.length > 0 假元素将被视为真元素所以我不想在那里保留那个假物品
      • 您的&lt;span data-id="47" class="close"&gt; 似乎是硬编码的。然后在您的refresh_chat() 函数中使用var last = $('.conversation li:not(.fake):last').data('id'); 这似乎表明您总是将last: 47 传递给您的“includes/router.php”页面。这个data-id 不应该是动态的并且基于发送/接收的最后一条消息的 id 吗?
      • 否,因为最后一条消息附加到父级,所以 :last 应该选择父级中的最后一个元素,因此是对话中的最后一条消息(已读)
      【解决方案5】:

      您遇到的问题与 setInterval 将每 3000 毫秒安排一次您的 refresh_chat 函数的新执行有关,无论当前是否正在进行执行。

      您应该做的只是自己调用一次refresh_chat,然后在内部回调函数的最底部(当有数据从服务器返回时调用),添加一个@ 987654324@。

      这将确保客户端在您知道它已完成工作之前不会尝试安排对 refresh_chat 函数的新调用。

      将其视为某种延迟递归调用(尽管从技术上讲,它不是一个,因为调用堆栈不会不断增加)。

      【讨论】:

        【解决方案6】:

        正如@zch 指出的那样,这个问题可能会在您的实际方法中出现。

        我建议两种方法:

        1.- 就在调用 refresh_chat() 之前,将最后一个帖子中止到服务器。通过这样做,如果在这 3 秒后响应没有发送到浏览器(或服务器根本没有响应),则您放弃最后一次尝试,并再次发送请求。这不是最好的方法,因为服务器资源可能会被永远不会接受的响应浪费掉。

        2.- 尝试制作一个循环计数器(例如从 0 到 10),随着每个请求递增并发送它,并让服务器将其发送回来,然后丢弃任何非重合响应。像这样的:

        Request with 0 -> Response with 0
        Request with 1 -> Response with 1
        Request with 2 -> (no response)
        Request with 3 -> Response with 2 (Discarded as it doesn't match)
        Request with 4 -> (no response yet)
                   -> Response with 3 (Discarded as 4 is already sent)
                   -> Response with 4 (Accepted, actual Request was with 4)
        

        希望你觉得这很有用。

        【讨论】:

        • 你好 Aitor,我怎样才能中止上一个帖子到服务器?
        • 好的,$.post() 方法应该返回一个 xhr 对象,它应该有一个 abort 方法。因此,您可以将其存储为全局变量,如下所示: xhr = $.post(....); (将 xhr 声明为全局变量,这意味着超出任何函数)。然后在一个周期性执行的函数中,再次调用 xhr.abort() 和 refresh_chat()。这应该中止最后一个请求并创建一个新请求。告诉我这是否有帮助,我会编辑我的答案。
        【解决方案7】:

        有几点,“未读”类似乎没有多大作用。无论如何,您都会自动刷新,何时阅读或未阅读私人消息?如果有人关闭聊天并稍后返回,他们应该收到所有消息还是只收到最新消息?只是需要考虑的事情......

        您的错误很可能是因为您使用了 setInterval 并重新发送了相同的请求,这里有一些代码可以纠正这个问题。请注意使用“last_id”变量来停止重复请求,以及仅在处理完成后调用 setTimeout。

        我建议您使用长轮询或彗星进行聊天。 Comet, long polling with jquery tutorial 如果你想成为最前沿的 HTML5 WebSockets,但你必须做一些服务器配置。

        function refresh_chat(){
            var last = $('.conversation li:not(.fake):last').data('id');
            if(last_id == last) {
                /* this request was already successfully processed */
                /* wait some more and try again */
                if(auto_refresh) window.setTimeout(refresh_chat, 3000);
                return;
            }
            $.ajax({
            type: 'POST',
            url: 'includes/router.php',
            data: { task: 'update_conversation',id: '<?=$_GET['conversationid']?>',last: last },
            success: function(data) {
                var recibidas = $(data).find('li');
                /* IF there are new entries */
                if (recibidas.length > 0) {
                    last_id = last;
                    /* Remove all fake entries */
                    $('.conversation li.fake').remove();
                    /* Append new entries */
                    $('.conversation').append($(data).filter('.notifications').html());
                }
                //
                if(auto_refresh) window.setTimeout(refresh_chat, 3000);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.log("Ajax Error: " + (textStatus||""));
                //
                if(auto_refresh) window.setTimeout(refresh_chat, 3000);
            }
            });
        }
        var auto_refresh = true;
        var last_id = -1;
        refresh_chat();
        

        【讨论】:

          【解决方案8】:

          这个 ajax 聊天根本不是解决方案。 You need to read this book。如果您没有 jabber 服务器,我会给您访问权限(用户注册更新等)。看书,然后联系我。这是 XMPP + Strophe 库聊天(谷歌和 Facebook 正在使用的)!所以最好重新开始学习新东西,然后修复 ajax 聊天中的错误!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-08-14
            • 2012-11-06
            • 2010-09-07
            • 1970-01-01
            • 2013-03-28
            • 2015-07-01
            • 2011-02-27
            • 1970-01-01
            相关资源
            最近更新 更多