上一篇文章介绍了 ajax 技术核心方法,和跨域的问题(只要后台支持跨域默认 post 就可以),这篇文章讲解一下使用 ajax 实现的轮询技术,至于 iframe,SSE 服务器单向推送,以及 webSocket 双工通道暂时不涉及。
一些概念:
短轮询:浏览器通过循环或者 setTimeout 方法,每隔一段时间往后台发送一次请求,无线循环
长轮询:不停的向后台请求数据,但是后台如果检测不到数据变动,就会将这个请求挂掉。如果检测到数据变动,就会响应这个请求变动数据
区别概念:
长连接:在进行 http 数据传输的时候,在数据传输层一直开着一个 TCP 通道,所有请求资源文件都是通过复用这个通道去请求数据,有超时时间
短连接:如果 http 进行的短连接,即每次浏览器发送请求,都会创建 TCP 通道,然后传输完成了再进行销毁,重复操作,消耗很大
主要区别:
主要业务方面:及时性比较高的应用(web 端聊天系统),或者需要后台等待响应的应用(比如付款,等待完成响应)。
关键代码:
- /*
- * 长轮询的实现
- * a. 业务上只需要得到服务器一次响应的轮询
- * b. 业务上需要无限次得到服务器响应的轮询
- *
- * param: url 请求接口地址
- * data 请求参数
- * successEvent 成功事件处理
- * isAll 是否一直请求(例如,等待付款完成业务,只需要请求一次)
- * timeout ajax超时时间
- * timeFrequency 每隔多少时间发送一次请求
- * error 错误事件
- * timeout 超时处理
- * */
- longPolling: function(url, data, successEvent, isAll, timeout, timeFrequency, errorEvent, timeoutEvent) {
- var ajaxParam = {
- time: timeout,
- type: "post",
- url: url,
- data: data,
- async: false,
- success: function(date) {
- successEvent(data);
- var timer = setTimeout(function() {
- tempObj.longPolling(url, data, successEvent, isAll, error, timeoutEvent);
- },
- timeFrequency);
- //业务需求判断,是否只需要得到一次结果
- if (!isAll) clearTimeout(timer);
- },
- //如果走了error说明该接口有问题,没必要继续下去了
- error: errorEvent,
- timeout: function() {
- timeoutEvent();
- setTimeout(function() {
- tempObj.longPolling(url, data, successEvent, isAll, error, timeoutEvent)
- },
- timeFrequency);
- }
- };
- ajax.common(ajaxParam);
- }
考虑到业务需求,集成了一次 isAll 参数有 2 个意义
稍微提及一下遇到的一些问题:
问题:
- success: function(date) {
- successEvent(data); //此处使用递归,不停递归自己
- tempObj.longPolling(url, data, successEvent, isAll, error, timeoutEvent);
- },
浏览器报错:
- Uncaught RangeError: Maximum call stack size exceeded.at Object.common(ajax - 1.2.js: 202) at Object.longPolling(ajax - 1.2.js: 280) at Object.success(ajax - 1.2.js: 266) at XMLHttpRequest.xhr.onload(ajax - 1.2.js: 160) at Object.common(ajax - 1.2.js: 202) at Object.longPolling(ajax - 1.2.js: 280) at Object.success(ajax - 1.2.js: 266) at XMLHttpRequest.xhr.onload(ajax - 1.2.js: 160) at Object.common(ajax - 1.2.js: 202) at Object.longPolling(ajax - 1.2.js: 280) common@ajax - 1.2.js: 202 longPolling@ajax - 1.2.js: 280 success@ajax - 1.2.js: 266 xhr.onload@ajax - 1.2.js: 160(anonymous)@index.html: 42(anonymous)@index.html: 43 ajax - 1.2.js: 202 Uncaught RangeError: Maximum call stack size exceeded.at Object.common(ajax - 1.2.js: 202) at Object.longPolling(ajax - 1.2.js: 280) at Object.success(ajax - 1.2.js: 266) at XMLHttpRequest.xhr.onload(ajax - 1.2.js: 160) at Object.common(ajax - 1.2.js: 202) at Object.longPolling(ajax - 1.2.js: 280) at Object.success(ajax - 1.2.js: 266) at XMLHttpRequest.xhr.onload(ajax - 1.2.js: 160) at Object.common(ajax - 1.2.js: 202) at Object.longPolling(ajax - 1.2.js: 280)
英文解释:
超出最大调用堆栈大小。
问题原因:
递归调用过多导致的栈溢出问题说明
问题解释:
函数调用的参数是通过栈空间来传递的,在调用过程中会占用线程的栈资源。而递归调用,只有走到最后的结束点后函数才能依次退出,而未到达最后的结束点之前,占用的栈空间一直没有释放,如果递归调用次数过多,就可能导致占用的栈资源超过线程的最大值,从而导致栈溢出,导致程序的异常退出。js 可以调用自身,这里不停的调用 longPolling 方法,在方法里面不停的调用自己,导致 GC(垃圾回收)一直不释放,越来越大,导致资源超过最大上限,直接崩溃。然后级联一层一层的抛出崩溃信息
解决方案:
使用 settimeout 解决该问题
方案解释:
因为 Javascript 是单线程的,有个排队的处理队列,所以 settimeout 相当于有一个计时器,不停的向这个队列每隔一段时间塞进一个处理事件。因为这样,相当于 longPolling 方法每次都走完了,GC 就将该方法的资源释放了,然后再执行,再释放。
代码已集成 github:https://github.com/GerryIsWarrior/ajax 点颗星星是我最大的鼓励,下一步研究 ajax 的上传文件技术(H5 的)
PS: 对于轮询这个技术,虽然平时用的少,但是在一些特殊的业务场景能发挥很大的作用。在浏览器,没有完完全全支持 H5 的境况下,这个还是要考虑的。毕竟 H5 的那些 webSocket 还是需要 H5 兼容的。而且,研究这一块,对原声 js,和计算机的一些底层技术还是很有帮助的,像堆栈溢出,不仅仅是前端,后端也会遇到。这样的话,自己底层更夯实,对于以后上层的发展也会有更好的增长。
来源: http://www.cnblogs.com/GerryOfZhong/p/6135288.html