12月14, 2016

js防止重复请求的处理

其实在知乎上已经有一个非常棒的答案了:

怎样防止重复发送 Ajax 请求

我之所以写这篇,主要是对这个答案的一个思考及具体实践。

昨天晚上莫名想到要用Koa来搭一个node服务器(主要是想学习一下Koa)。于是早上开始找文章,各种尝试,终于在中饭前,把一个简单的项目给跑起来了。

项目地址:hello-koa 。噢对,我开始在项目用yarn,尝试一下也未尝不可。

通过Koa,我觉得可以学以下几点知识:

  • 了解中间件,能学会async await的使用方法(我觉得这货比function *{}要好理解),能写一个简单的sleep功能即可。
  • koa-static
  • koa-route
  • koa-json
  • 模板引擎(如nunjucks、jade等),当然我上面的项目有点装B了,只是简单的吐出一个页面而已,完全可以用filereadstream来pipe出页面。

搞定了后端,那么现在来搞前端吧。

防止重复刷新一般有下面三种场景:

情景一(用得最多的)

ajax请求还没有发送成功,再次点击。

这种情形往往出现在某个请求时长较长,用户觉得是不是网络坏了,于是又拼命点击,造成请求很多,带给服务器一定的压力。

以下给出jquery的方案。

var common = {
    /**
     *
     * @param option jquery.ajax的option参数
     * @param context 上下文 object 上下文,通常为点击的DOM元素
     * @param useFirstReq boolean 是否使用第一次请求,如果false,则abort掉之前的请求
     * @param tipFunction 当useFirstReq为true时,要触发的函数,比如提示用户不能频繁点击
     * @returns {*}
     */
    ajax: function(option, context, useFirstReq, tipFunction) {

        if(context == null ) {
            console.error("第二个参数:上下文必须传");
            return;
        }

        if(typeof context == "boolean" || typeof context == "function") {
            console.error("上下文不能是boolean类型或者function类型");
            return;
        }

        if (useFirstReq == null) {
            useFirstReq =  true;
        }

        if(typeof useFirstReq == "function") {
            tipFunction = useFirstReq;
            useFirstReq = true;
        }

        if (context.promise_ && context.promise_.state() === "pending") {

            if (useFirstReq) {
                tipFunction && tipFunction();
                return;
            }else {
                context.promise_.abort();
            }
        }
        return context.promise_  = $.ajax(option);

    }
}

用法:

common.ajax({}, DOM元素(唯一性), function(){})
common.ajax({}, DOM元素(唯一性), false)

第一种的写法,是在当前的请求pending时,不触发后面的请求,function是一个函数,用来做一些用户提示,告知用户不要频繁操作。函数可缺省。

第二种的写法,是指只要最后一个请求,之前的请求都作废。

容我说明一下,知乎上的那个哥们写的这个代码:

module.submit = function() {
  if (this.promise_.state() === "pending") {
      return;
  }
  return this.promise_ = $.post("/api/save")
}

其实是有点问题的,比如说两个按钮,都有各自的ajax。上面的写法会造成,如果有一个按钮的请求在pending,那么另一个按钮的请求就发不出去。

解决的办法,就是要传入上下文,这个上下文保证唯一性即可,比如传入当前点击的DOM元素。

之前也有考虑过用option里面的URL来代替上下文,但不同的按钮,可以是相同的URL请求。且请求可能带有随机参数,不能保证唯一性。

情景二

在某段时间内,只能发一次请求。通常用于autocomplete,或者resize触发ajax等场景。

它不像场景一,不需要阻止或者把之前的abort。

伪代码如下:

module.submit = throttle(150, function() {
  // todo
})

具体实践,等后续再补充。。

情景三

请求成功之后,需要防止多次触发。

伪代码如下:

//大于2s
module.submit = debounce(2000, function() {
  // todo
})

我们常常可以在论坛里面,看到一篇文章发表之后,或者评论某篇文章之后,多长时间内不准再评论。或者一天只能评论多少条。

如果超出多少条,会对账号进行一定的处理,如封号等。

具体实践,等后续再补充。。

本文链接:www.my-fe.pub/post/js-prevent-duplicate-requests.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。