2012-11-07 33 views
3

这里是我的情况:jQuery的投票聊天 - 重复条目

  • 每当用户发送新邮件,我追加到会话线程作为预览而一个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)或.unread(这意味着接收方刚刚收到了第电子邮件)

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

这可能是什么原因造成的? (我只是不停地读书,但我找不到任何奇怪...)

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

CNC中

$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参数(至极代表显示给用户的最后一个消息)

+5

与轮询阿贾克斯请求聊天真的不是最佳实践。 – gdoron

+0

你的'includes/router.php?task = update_conversation'脚本如何确保不发送一个条目两次? – Bergi

+0

它没有。因为在数据库中没有重复@Bergi(gdoron)所以你有什么建议? –

回答

0

我想这是因为你只“隐藏”了假的条目,如果有新的要显示的内容:

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

我建议把这个从if语句中拿出来。似乎你应该有remove()在你发布一条新消息之后,比如你的提交函数function(data,response){也许。

+0

我不这么认为。我实际上是删除假元素(而不是隐藏!!!我没有看到你得到的那个)。但我应该是因为假元素会被recididas.length> 0所以会被当作真正的元素。所以我不想在那里保留那个假项目 –

+1

它看起来像你的'是硬编码的。然后在你的'refresh_chat()'函数中使用'var last = $('。conversation li:not(.fake):last')。data('id');'这似乎表明你总是通过'last:47'到你的'includes/router.php'页面。这个'data-id'不应该是动态的,并且基于发送/接收的最后一个消息的ID吗? – davehale23

+0

否,因为最后一条消息是继承父对象,所以:最后应选择父对象中的最后一个元素,因此会话中的最后一条消息(重新登录) –

3

想一想情形:

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

解决此问题的最简单方法是删除setInterval,并在处理响应后添加setTimeout。

+0

对此进行了测试,它仍然会得到重复... –

-1

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

1

给Socket.io一试!即使您将消息发送到服务器,也不会有任何明显的延迟,在那里解析并转换它并将其发回,从而不需要预览。

对于聊天应用程序使用Ajax和setTimeout只会导致你痛苦。

+0

任何适用于我的问题? –

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; 

最后,没有按投票看起来并不是实现聊天应用的最佳方式。 也许一个套接字和轮询的组合(如果浏览器不支持套接字)会更好。 最佳 Ç

0

您有相关的事实,setInterval将安排您refresh_chat功能的新鲜执行每3000毫秒,无论是否有当前执行回事与否的问题。

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

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

把它看作是一种推迟的递归调用(尽管在技术上它不是一个,因为调用堆栈不会增加)。

0

正如@zch指出的,这个问题可能会随着您的实际方法而提高。

我建议2点的方法:

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) 

希望你觉得这个有用。

+0

你好Aitor,我怎样才能中止服务器的最后一篇文章? –

+0

好吧,$ .post()方法应该返回一个xhr对象,它应该有一个中止方法。因此,您可以将其存储为全局变量,如下所示:xhr = $ .post(....); (声明xhr是一个全局变量,这意味着超出了任何函数)。然后在一个定期执行的函数中,再次调用xhr.abort()和refresh_chat()。这应该中止最后的请求并创建一个新的请求。告诉我,如果这有帮助,我会编辑我的答案。 –

0

有几点,'未读'类似乎没有太多。无论如何,你是在自动刷新,何时是私人信息读取还是未读?如果有人关闭聊天并稍后再回来,他们应该得到他们的所有消息还是只有最新的消息?只是需要考虑的事情...

你的bug可能是因为你使用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(); 
+0

未读类仅用于设计,以应用不同的背景色。 –