深入理解promise

前言

互联网时代,数据的流动十分频繁,并且为了提升用户体验,许多数据与模版都放在服务端。这也就导致了一个问题:异步请求过多。加之node.js(使用单线程异步编程模式)的火爆,异步编程使用逐渐多了起来。如果不能好好控制代码逻辑,我们将陷入无尽的回调地狱中。
  

ECMAScript 6也有把异步操作纳入规范,异步编程必将是一个趋势。

什么是Promise

1. 我们先来看看以前是怎么样处理异步请求的吧。
$.get(url1,function(data1){
    $.get(url2,function(data2){
        $.get(url3,function(data3){
            $.get(url4,function(data4){

            });
        });
    });
});
每个异步请求都需要上一个请求完成之后才能执行,这就导致了代码十分不整洁。如果想要优雅地处理这个问题,我们肯定是想有一个方便的方法来不用写这么多缩进。是否可以有一个任务管理机制来管理各个异步回调呢?假如可以像下面这样:
var promise = new Promise(request1);
    //将各个请求放入事件队列
    promise.then(request2);
    promise.then(request3);
    //通过start()方法启动整个任务
    promise.start();
这样看起来是不是优雅多了,整洁多了?当然,不仅仅是为了优雅和整洁,下面将介绍这种管理方式的其他作用。

Promise的概念

promise有三种状态:
1. 已经完成了:resolved
2. 出错中断了:rejected
3. 等待上个事 件:waiting
如果任务A出错了,将会把自身标记为rejected,如果后续的任务强依赖于任务A,也将会把自身标记为rejected。当然,我们需要捕获出错任务的出错信息,也就是返回一个errMsg。

jQuery的Deferred对象(转载自http://www.web-tinker.com/article/20154.html

jQuery的方法链应该都是耳熟能详的东西了,从jQuery最早的版本开始,jQuery对象基本都是基于方法链使用的。但是在早期并没有明显的异步方法链这个概念,异步方法的回调都是作为参数传入的。直到jQuery 1.5才引入Deferred对象来解决了这个异步问题。
  先来看一个例子

$.post("test.php").done(function(){
  //成功时的操作
});
  done的参数是这个post请求成功后的回调函数。post本身是异步的,而done却是在这个异步方法之后以方法链的方式调用的。这整个就是一个异步方法链。而我们在jQuery源代码的ajax(post是ajax的派生方法)这个函数中可以找到它的返回值是一个Deferred对象(development jQuery 1.9.1 第7753行)。也就是说,post这个方法实现这样的异步方法链就是使用了Deferred这个对象。而jQuery本身也提供这个Deferred对象,有详细文档(在文章末尾有链接)。我们了解了这个Deferred对象,就可以在jQuery的一些异步方法中使用更复杂的异步方法链操作,也可以在自己定义的函数中使用这个对象以加入异步方法链的功能。
  既然jQuery有提供详细文档并且有例子了,我就不说太多了,简单的把这个对象的各个方法整理一遍吧。其实,这个对象的方法分为两类。一类是内部调用的,就比如上面这个例子在post这个方法的内部调用Deferred对象上的方法,就勉强叫做“私有方法”吧。另一种就是类似上面的done,是链式调用时使用的,咱就称为“公共方法”了(叫法有点牵强,不喜勿喷,理解就好)。咱先来看看私有方法。
  deferred.resolve(args)
  deferred.reject(args)
  这两个方法是最关键的两个私有方法,调用resolve时表示异步处理成功,调用reject时表示异步处理失败。它们调用时就相当于触发了在方法链中绑定的相关回调函数。比如resolve调用时候,就会触发方法链条上用done绑定的回调函数,因为resolve是表示处理成功,而done绑定的回调函数表示处理成功时的动作。reject也有类似的相关方法叫做fail,这个我们后面再说了。另外,这个两个函数的参数就是传递给回调函数的参数。
  deferred.resolveWith(context,[args])
  deferred.rejectWith(context,[args])
  这两个方法其实就是上面两个方法的派生方法,上面两个方法是直接调用回调函数并传入参数,而这两个方法则是使用apply的模式调用回调函数。也就是说,context这个参数会被作为回调函数中的this来使用,而args必须是一个数组。
  deferred.notify(args)
  deferred.notifyWith(context,[args])
  这两个方法的关系和上面两组方法的关系一样。它们对应的绑定回调函数的方法是progress。之所以不把这个也放入上面两组中是因为这个函数的含义是在异步处理过程中回调,区别于上面两个方法是异步处理完成时回调。也就是说,在一次异步处理中可以调用多次notify,但是只能调用一次resolve或reject。其实这个方法的语言含义是发出一个通知,这很容易理解吧。另外这个方法是jQuery 1.7才加入的,之前的那些在1.5版本就有了。
  deferred.promise([target])
  最后还有一个很重要的promise方法。我们之所以要区分开私有方法和公共方法就是因为它。这个方法的功能是创建一个原deferred的副本,这个副本中的方法和原对象的方法是映射关系,但是这个副本只包含公共方法。这个方法的返回值就是它创建的副本对象。这个方法的存在是为了防止私有方法在外部被调用。它的参数是一个对象,这个对象上的属性会被复制一份到他创建的副本对象上。这个可以避免我们自己手动去给返回值添加属性的麻烦。
  以上这些就是Deferred的私有方法了,下面来看看公共方法。
  deferred.done(callbacks)
  deferred.fail(callbacks)
  deferred.progress(callbacks)
  这三个方法在前面的私有方法中已经涉及到了,分别对应什么私有方法我就不重复说了。它们的功能是绑定各种操作的回调函数,参数可以是多个,也可以是一个,但是都是函数或函数的数组就对了。另外,它们可以调用多次,绑定的函数也会叠加上。
  deferred.always(callback[,callback])
  这个方法也是绑定回调函数,但是它并不是绑定到某个操作上。当Deferred对象的异步处理完成时候就会触发它,注意这里是完成而不是成功或失败。无论成功还是失败都会完成,也就是说无论内部是调用resolve还是reject它都会被触发(调用notify不会触发,因为它没完成)。这个方法是jQuery 1.6加入的。
  deferred.then(callback[,callback[,callback]])
  这个then方法在jQuery中的修改次数比较多,很多版本的参数都不同。1.6时它有两个参数,而1.8时候可以有3个参数。不过这些参数都是函数,其实这个then方法就是上面done、fail、progress,这三个方法的快捷绑定方式。调用then传入三个参数就会分别被当作是done、fail、progress,这三个方法的参数使用而把参数绑定到相应的动作上。注意jQuery的版本和参数的顺序就没什么问题了。
  deferred.pipe(callback[,callback[,callback]])
  这个方法的功能是把绑定的动作回调函数分组处理,这是我自己对它的理解,官方文档描述的更奇葩,我也看的一头雾水。它的三个参数和then类似,也是对应到三个动作的。但只是这个三个参数不是动作的回调函数,而是对动作回调函数参数的预处理函数。感觉说的有点绕了,其实很简单。这个我还是写个例子吧。这里就拿done和resolve举例

//创建一个Deferred对象
var def=new $.Deferred;
//直接调用成功,并传入参数
def.resolve(3);

//第一组回调
def.pipe().done(function(e){
  console.log("第一组第一个:"+e);
}).done(function(e){
  console.log("第一组第二个:"+e);
});

//第二组回调
def.pipe(function(e){
  return e*3;
}).done(function(e){
  console.log("第二组第一个:"+e);
}).done(function(e){
  console.log("第二组第二个:"+e);
});
  这个例子就是把回调函数分成两组,第一组pipe没有参数,所以直接把resolve的参数穿给done绑定的函数;第二组由于pipe的第一个参数是存在的,所以resolve的参数先放入pipe的回调中处理,让后才把返回值传给done绑定的回调函数。另外,如果是resolveWith触发的,this在done绑定的回调函数中也依然有效。
  deferred.isRejected()
  deferred.isResolved()
  这个两个方法简单,从字面上就能理解。返回一个布尔值判断当前deferred对象是否已经成功或失败。
  deferred.state()
  这个方法也很简单,获取当前deferred对象的状态,返回的是字符串,共有三种情况。pending:未完成,resolved:已成功,rejected:已失败。
  以上这些就是公共方法了。除了私有方法和公共方法外,还有个特殊的是直接在jQuery根对象上和Deferred相关的方法,就是下面这个when方法
  jQuery.when(deferreds)
  这个东西比较特殊吧,它的参数是一个或多个Deferred对象,返回一个新的Deferred对象。它的功能是把参数中这些Deferred对象合并。怎么合并呢?这个有点麻烦,由于都是Deferred对象,下面简称为对象。只有所有对象都完成是才会触发完成,而任意一个对象发出通知时就会触发通知。完成以后呢?只有所有对象都成功是才会触发成功,而只要有任意一个对象失败时候就会触发失败。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值