---
title: "jQuery实现长轮询"
url: "https://lerry.me/post/2015/01/jquery-implementation-long-polling"
date: 2015-01-08T11:53:00.000Z
updated: 2026-08-31T08:27:11.987Z
tags:
  - "Long Polling"
  - "jQuery"
language: zh-CN
---

# jQuery实现长轮询

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

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

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

```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这里执行了函数体本身，以上过程周而复始，这样就能保持与服务器的长久连接了，但是问题来了，

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

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

```coffeescript
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函数会被回调，请求会继续发，直到地老天荒。到这里，我们已经很好的实现了预期的功能。

注意：

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

参考文章：

[HTTP 双向通信草案](http://xmpp.org/internet-drafts/attic/draft-loreto-http-bidirectional-00.html#timeouts)
