jQuery轮询聊天 - 重复条目

时间:2012-11-07 20:52:21

标签: javascript jquery setinterval

以下是我的情景:

  • 每当用户发送新消息时,我会将其作为预览附加到会话线程,而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);
    }                       
});

如您所见,<li>项可能有两个类:.fake(这意味着此项目是用户刚刚提交的内容的预览,并且已被js追加)或{ {1}}(这意味着接收者刚收到消息)

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

可能导致这种情况的原因是什么? (我只是继续读它,但我找不到任何奇怪的东西......)

PD:基本上,有些消息不止一次出现:S

- 编辑 -

.unread

此查询是$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()在JavaScript参数中是最后一个(代表向用户显示的最后一条消息)

8 个答案:

答案 0 :(得分:3)

考虑场景:

  1. refresh_chat() - 发送到服务器的请求
  2. 3秒钟通过
  3. refresh_chat() - 发送给服务器的相同请求
  4. 第一次收到回复。新条目已添加。
  5. 对收到的第二个回复。相同的条目再次添加。
  6. 解决此问题的最简单方法是删除setInterval,并在处理响应后添加setTimeout。

答案 1 :(得分:1)

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

对聊天应用程序使用Ajax和setTimeout只会让你感到痛苦。

答案 2 :(得分:1)

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

此外,您的数据库查询非常容易受到SQL注入攻击http://en.wikipedia.org/wiki/SQL_injection  (例如,提供最后一个值为: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;

最后,轮询似乎不是实现聊天应用的最佳方式。 也许套接字和轮询的组合(如果浏览器不支持套接字)将更好地工作。 最好 ç

答案 3 :(得分:0)

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

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

我建议从if语句中取出这个。您发布新邮件后似乎应该有remove(),例如在提交函数的function(data,response){内。

答案 4 :(得分:0)

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

你应该做的只是简单地自己调用refresh_chat,然后在内部回调函数的最底层(当有数据从服务器返回时调用的那个),你添加一个setTimeout(refresh_chat, 3000)

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

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

答案 5 :(得分:0)

正如@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)

希望你觉得这很有用。

答案 6 :(得分:0)

有几点,“未读”的课程似乎并不多。您是否自动刷新,何时读取或未读取私人消息?如果有人关闭聊天并稍后返回,他们应该收到所有邮件还是最新邮件?只是要考虑的事情......

你的错误可能是因为你使用了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();

答案 7 :(得分:-1)

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