jquery实现长轮询

由于http的request-response模式,一般来说只能由客户端发起请求,而服务器端是无法主动将消息推送到客户端的,但是聪明的工程师们还是想出来了很多办法来实现双向通信,如html5带来的websocket或者本文要讲的一种更简单的实现方法,长轮询。

我们都用过微信web版的扫描登录,如果你打开过控制台,就会发现,页面不断的向后台发起请求,如果你不扫描二维码,这个请求会在大概27秒后得到响应,然后重发一个请求,周而复始。如果你扫描了二维码,这个请求就会很快得到响应,这里就用到了长轮询。

一个典型的ajax长轮询请求一般是这样的(本文中的代码均为coffeescript)

callback = (o) ->
    doSomethingHere()
(poll = ->
  xhr = jQuery.ajax url,
    type: method,
    dataType: "json",
    data: data,
    timeout: 30000
    success: (o) ->
      callback and callback(o)
    complete: ->
      poll()
)()

通过查阅jquery文档,我们可以知道,complete函数在一个ajax请求完成时会被触发,不管这个请求成功与否。 这里我们设置了30秒的超时,如果服务器在30内响应,我们的回调函数就会执行(比如聊天室更新聊天记录什么的),然后回调complete函数,如果响应超时,同样会回调complete函数,我们在complete这里执行了函数体本身,以上过程周而复始,这样就能保持与服务器的长久连接了,但是问题来了,

如果我们要终止长轮询该怎么做?

如果页面发生了跳转,代码自然就不会再继续执行,但是如果没有跳转呢?经过探索,代码变成了这样

poll = (method, url, data, callback) ->
  if jQuery.isFunction data
    callback = data
    data = {}
  _poll = ->
    jQuery.ajax url,
      type: method,
      dataType: "json",
      data: data,
      timeout: 30000
      success: (o) ->
        callback and callback(o)
      error: (xhr, msg, e) ->
        if msg == "timeout"
          _poll()

(xhr = poll "get", "/", (o) ->
  if o.success
    console.log o
  else
    xhr()
)()

这里poll会返回一个函数,函数执行时开始进行ajax请求,如果服务器在超时前响应了,我们会根据服务器的响应内容决定是否继续发请求,如果超时了,error函数会被回调,请求会继续发,直到地(wang)久(ye)天(guan)荒(bi)。到这里,我们已经很好的实现了预期的功能。

注意:

  • error的触发可能是timeout以外的事件
  • 超时时间理论上越长越好,但是30秒是个安全值,你可以在客户端设置30秒超时,服务器在27秒的时候响应

参考文章:

http://xmpp.org/internet-drafts/attic/draft-loreto-http-bidirectional-00.html#timeouts

26,083 views, since 2015-01-08