对程序员的一个 Promise(二)

969 阅读2分钟
原文链接: xieyufei.com

  在上一篇文章《对程序员的一个Promise(一)》中,分享了一下了ES6中Promise的用法,但是需要浏览器支持Promise。在jQuery中也有Promise,就让我来看看jQuery中的Promise是怎么用的。

jQuery中的Promise

  在jQuery中,首先要通过Deferred方法获取到Deferred对象,让我们来打印出来看看它有什么方法。

var d = $.Deferred();
console.log(d);

  最后输出如下:
jQuery-Promise

  我们可以看到$.Deferred()返回是一个对象(Deferred对象),也有resolve、then、reject等一些我们熟悉的方法,让我们来看看它是怎么用的。

基本用法

function getPromise1(){
  var d = $.Deferred();
  setTimeout(() => {
    console.log('异步1结束');
    d.resolve('异步1数据');
  }, 200);
  return d;
}
var def = getPromise1();
def.then((data) => {
  console.log(data);
});

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  感觉跟ES6中Promise的用法很相似。我们首先通过$.Deferred()获取到了Deferred对象,然后在异步成功后返回数据,然后在then方法中对异步数据进行处理。
  但是跟ES6中不一样的是,异步没有放到Promise的构造函数中,在异步成功后,调用了Deferred对象的resolve方法。then方法处理回调数据还是一样的。

发现问题

  既然Deferred对象上有resolve()方法,那么是不是在外部就能够调用resolve()方法就能够修改Promise的状态呢。把上面的代码进行如下改写:

function getPromise1(){
  var d = $.Deferred();
  setTimeout(() => {
    console.log('异步1结束');
    d.resolve('异步1数据');
  }, 300);
  return d;
}
var def = getPromise1();
def.then((data) => {
  console.log(data);
});
def.resolve('外部数据1');

  输出结果如下:
result1.png
  可以看到我们在函数的外面调用了resolve()方法提前让异步结束并且返回了数据。这样Promise的状态就能够被随意的改变,肯定是不行的。

解决问题

  将代码进行如下改进,在返回的对象上多加一个promise()方法:

function getPromise1(){
  var d = $.Deferred();
  setTimeout(() => {
    console.log('异步1结束');
    d.resolve('异步1数据');
  }, 300);
  return d.promise();
}
var def = getPromise1();
def.then((data) => {
  console.log(data);
});
def.resolve('外部数据1');

  输出结果如下:
result2.png
  这时候,如果在函数的外部调用resolve()方法会报错,告诉我们resolve()方法不存在,异步也会“如期”执行完成。如果我们将def对象打印出来看的话会发现并没有resolve()方法。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

成功和失败的回调

  和ES6中的Promise相似,then也支持接收两个参数,分别是执行成功的回调和执行失败的回调。

function getPromise1(){
  var d = $.Deferred();
  setTimeout(() => {
    var number = Math.round(Math.random()*10);
    if(number %2 == 0) {
      d.resolve('成功数据');
    } else {
      d.reject('失败数据');
    }
  }, 300);
  return d.promise();
}
var def = getPromise1();
def.then(
  (data) => {
    console.log('成功回调');
    console.log(data);
  },
  (data) => {
    console.log('失败回调');
    console.log(data);
  }
)

  除此之外,jQuery还新增了两个函数,done()和fail()分别用来指定成功的回调和失败的回调。因此,上面的代码和下面的代码是等价的:

var def = getPromise1();
def.done((data) => {
  console.log('成功回调');
  console.log(data);
});
def.fail((data) => {
  console.log('失败回调');
  console.log(data);
});

链式调用

  既然是Promise,那么then()肯定也支持链式调用的,这边也不在赘述,跟ES6中是一样的用法,不太熟悉的可以戳这边《对程序员的一个Promise(一)》

扩展函数

  jQuery中没有all和race方法,但是扩展了一些其他的方法。

always方法

  always方法就是不管执行成功或者失败,都会执行的,有点类似ajax的complete方法。

var def = getPromise1();
def.done((data) => {
  console.log('成功回调');
  console.log(data);
});
def.fail((data) => {
  console.log('失败回调');
  console.log(data);
});
def.always(() => {
  console.log('总是执行')
})

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

when方法

  when方法和ES6中的all方法功能一样,都是并行执行异步,所有异步执行完成后才执行回调函数。不过when方法是挂载在全局中的方法,而且,它接受的参数也是多个对象。

function getPromise1(){
  var d = $.Deferred();
  setTimeout(() => {
    d.resolve('成功数据1');
  }, 100);
  return d.promise();
}
function getPromise2(){
  var d = $.Deferred();
  setTimeout(() => {
    d.resolve('成功数据2');
  }, 200);
  return d.promise();
}
function getPromise3(){
  var d = $.Deferred();
  setTimeout(() => {
    d.resolve('成功数据3');
  }, 300);
  return d.promise();
}
$.when(getPromise1(), getPromise2(), getPromise3())
.then((data1,data2,data3) => {
  console.log('执行完成');
  console.log(data1, data2, data3);
})

理解ajax的本质

  平时我们都是这么请求ajax异步的:

$.ajax({
  url:'test.json',
  success:function(data){
    // ...
  },
  error:function(data){
    // ...
  }
});

  上面的代码,平时的工作中我们肯定也写了无数遍了,已经很熟悉了,但是这个ajax()方法返回的是什么呢,让我们打印出来看下:

ajax_result.png

  难道是巧合么?我们看到了熟悉的always()、done()、fail()、promise()、then()等方法。没错,ajax返回的也是一个Deferred对象,既然是Deferred对象,那么肯定也支持链式调用了。
  那么将ajax方法进行如下改写:

function getAjax1(){
  var def = $.ajax({url:'test.json'});
  return def.promise();
}
var def1 = getAjax1();
def1.then((data) => {
  console.log(data);
});

  既然是Deferred对象,那么done()、fail()、$.when()这些方法也能使用,这里就不再赘述了。

本文地址: xieyufei.com/2017/01/28/…