使用jQuery中Deferred异步对象构建顺序执行队列

在前面,有一篇文章介绍了jQuery的异步对象Deferred,通过他,我们可以实现一些操作比如回调函数在异步操作(耗时)完成之后再执行。比如这样的场景,我们在编辑页面,对一个产品对象进行编辑,而该对象一些属性比如所属渠道,是可以下拉选择,而所属渠道不是固定不变的,而是根据后台接口channel/getAll返回给前台页面。这时候,我们需要先初始加载渠道这个下拉框,然后根据产品自己的属性值的渠道来反显渠道initForm()。这是一个很常见的场景。如果在渠道下拉框没有初始化完成之前,产品渠道先反显了,那么很有可能最后又初始化渠道下拉框的时候,将这个反显的值冲掉了。

让一个操作在另一操作之后执行,我们可以使用嵌套的方式,让一个异步接着另一个异步,这样虽然可以解决问题,但是却造成了另外一个callback hell的问题。代码可读性差。

为此,我们需要用到异步对象来让完成这个操作。

function getAll(){

    var dtd = $.Deferred();

    $.ajax({

          url:"channel/getAll",

          success:function(){xxx;dtd.resolve();xxx}

    });

    return dtd.promise();
}

function initForm(){xxx;}

$.when(getAll()).done(initForm);

这样,就解决了我们的问题,初始化表单会在初始化渠道getAll()方法完成之后进行。

在实际的场景中,还有可能有这样的需求,几个耗时操作,需要按照顺序执行。我们知道$.when()方法,可以运行多个耗时的异步操作[d1,d1,d3],但是这些耗时操作是同时执行的,只有done()里面的方法会在这些异步操作[d1,d2,d3]都执行完成之后,才执行。

function d1(){
        var dtd = $.Deferred();
        console.log("d1 : -> start");
        setTimeout(function(){
              console.log("d1 : -> end");
              dtd.resolve();
        },1000);
        return dtd.promise();
}
function d2(){
        var dtd = $.Deferred();
        console.log("d2 : -> start");
        setTimeout(function(){
              console.log("d2 : -> end");
              dtd.resolve();
        },2000);
        return dtd.promise();
}
function d3(){
        var dtd = $.Deferred();
        console.log("d3 : -> start");
        setTimeout(function(){
              console.log("d3 : -> end");
              dtd.resolve();
        },3000);
        return dtd.promise();
}

console.log("all: -> start");

$.when(d1(),d2(),d3()).done(function(){

  console.log("all: -> end");

});

这样执行的结果是:

想要让他们顺序执行,我们需要在每一个函数执行之后调用done(),然后接下来执行下面的操作。

console.log("all: -> start")
d1().done(function(){
      d2().done(function(){
           d3().done(function(){
                console.log("all: -> end");
           });
      });
});

这样,虽然是按照顺序执行队列,但是还是回到了最初我们提到的callback hell。代码不够优雅。这里可以利用Array的一个reduce方法,让代码可读性更好,而且也能达到我们的预期,异步操作按照顺序执行。这种方式也不再使用done()后面接下一个函数的方式,而是采用then()来构建一个异步队列。reduce(function(){},$.Deferred().resolve())有两个参数,第二个参数是默认值。当我们第一次执行then()时,prev是没有值的,需要传入一个默认的promise对象,就是$.Deferred.resolve()。

function app(){
      var tasks = [d1,d2,d3];
      return tasks.reduce(function(prev,next){
              return prev.then(next);
       },$.Deferred().resolve());
}

$.when(app()).done(function(){

      console.log("all: -> end");

});

最终的演示效果如下:

 这样通过jQuery的Dererred对象,我们成功的构建了一个顺序执行队列。完整示例代码:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>deferred-reduce</title>
    <script src="js/jquery/jquery-1.12.4.min.js" type="text/javascript"></script>
  </head>
  <body>
     <script type="text/javascript">
        $(function(){
          function d1(){
            var dtd = $.Deferred();
            console.log("d1 : -> start");
            setTimeout(function(){
              console.log("d1 : -> end");
              dtd.resolve();
            },1000);
            return dtd.promise();
          }
          function d2(){
            var dtd = $.Deferred();
            console.log("d2 : -> start");
            setTimeout(function(){
              console.log("d2 : -> end");
              dtd.resolve();
            },2000);
            return dtd.promise();
          }
          function d3(){
            var dtd = $.Deferred();
            console.log("d3 : -> start");
            setTimeout(function(){
              console.log("d3 : -> end");
              dtd.resolve();
            },3000);
            return dtd.promise();
          }
          function app(){
            var tasks = [d1,d2,d3];
            return tasks.reduce(function(prev,next){
              return prev.then(next);
            },$.Deferred().resolve());
          }
          
          console.log("all: -> start")
          /*
          d1().done(function(){
            d2().done(function(){
              d3().done(function(){
                console.log("all: -> end");
              })
            })
          });*/
          $.when(app()).done(function(){
               console.log("all: -> end");
          })
        });
     </script>
  </body>
</html>

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffy5459

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值