ES6 Promise介绍

2,358 阅读2分钟
  • 作者:Leo晓华

回调地狱

由于 JavaScript 的单线程性质,我们必须等待上一个请求返回结果后才能处理下一步,如下:

$.ajax({ 
  url: "/step1", 
  success: function(){
      $.ajax({ 
        url: "/step2", 
        success: function(){
            $.ajax({ 
              url: "/step3", 
              success: function(){

              }
            });
        }
      });
  }
});

这种回调地狱嵌套层级多了,代码结构就容易变得很不直观,可读性比较差。
Promise 是 ES6原生支持的,把原来嵌套的回调改为了级联的方式。

了解Promise

Promise 可以简单理解为一个事务,这个事务存在三种状态:

  • 已经完成了 resolved(完成态)
  • 因为某种原因被中断了 rejected(失败态)
  • 初始状态 pending(未完成)

注意,这种状态的改变只会出现从未完成态向完成态或失败态转化,不能逆反。完成态和失败态不能互相转化,而且,状态一旦转化,将不能更改。

只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思是承诺,表示其他手段无法改变。

例子:

var p = new Promise(function (resolve, reject) {
    if(/* 异步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});

在声明一个Promise对象实例时,我们传入的匿名函数参数中:
resolve 就对应着完成态之后的操作
reject 对应着失败态之后的操作

Promise的then方法

p.then(function (value) {
    // 完成态,value是上面resolve传入的值
}, function (error) {
    // 失败态,error是上面reject传入的值
});

then()方法传递的两个参数中:

  • 第一个参数(函数)对应着完成态的操作,也就是resolve时调用

  • 第二个参数(函数)对应着失败态的操作,也就是reject时调用

  • 第二个参数可以没有

多个promise链式

例子1:

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve('p1'), 1000);
});

p1.then( ret => {
    console.log(ret); //p1
    return'then1';
}).then( ret => {
    console.log(ret); //then1
    return'then2';
}).then( ret => {
    console.log(ret); //then2
});

在 resolve 之前,promise 的每一个 then 都会将回调函数压入队列,resolve 后,将 resolve 的值送给队列的第一个函数,第一个函数执行完毕后,将执行结果再送入下一个函数,依次执行完队列。一连串下来,一气呵成,没有丝毫间断。
链式中的then方法(第二个开始),它们的resolve中的参数是什么?答案就是前一个then()中resolve的return语句的返回值。

例子2:

    step1:function(){
        var promise = new Promise(function(resolve,reject){
             $.ajax({
                url:"/step1",
                    success:function(data){
                        resolve(data)//在异步操作成功时调用
                    }
            });
        })
        return promise;
    },
    step2:function(val){
        var promise = new Promise(function(resolve,reject){
            $.ajax({
                url:"/step2",
                    data:val,   //来自step1的参数
                    success:function(data){
                        resolve(data)//在异步操作成功时调用
                    }
                });
            })
            return promise;
        }
    step1()
    .then(data => return step2(data))  //step1的结果传给step2作为参数
    .then(data => console.log(data))

错误处理

方法1:由then的第二个处理函数处理错误

var p = new Promise(function (resolve, reject) {
    // ...
    if(/* 异步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});

p.then(function (value) {
    // 完成态
}, function (error) {
    // 失败态
});

方法2:链式错误处理 (catch 方法)

var p = new Promise(function (resolve, reject) {
    // ...
    if(/* 异步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});

p.then(function (value) {
    // 完成态
}).then(function (value) {
    // 完成态
}).catch( err => {// 可以捕抓到前面的出现的错误。
    console.log(err.toString());
});

如果第一个then报错,第二个then不会执行。

一个活动项目的例子:

//查用户信息
querymystate(){
    var promise = new Promise(function(resolve,reject){
        utils.get('/querystate', {
          raffle_code:xxx,
        }, (rst) => {
          if (rst.return_code === 0) { //成功获取用户信息
                resolve(rst.data);                        
          } else { //获取失败
              reject(rst.error_message);    
          }
        })    
    })    
    return promise;
},
//我要领奖,必须先获取用户信息
getAward(){
    querymystate()
    .then(data => {
      //领奖处理
    }).catch(ErrMsg => {
      //弹出错误提示ErrMsg
    })
}

Promise.all()

Promise.all()方法用于将多个Promise实例,包装成一个新的Promise实例,例如:

var p = Promise.all([p1, p2, p3]);

新的Promise实例p的状态由p1, p2, p3决定:

  • 当p1, p2, p3的状态都为完成态时,p为完成态。

  • p1, p2, p3中任一一个状态为失败态,则p为失败态。

例子:

let a = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(2) 
    }, 2000) 
  }) 

let b = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(3) 
    }, 2000) 
  }) 

Promise.all([a, b])
.then( (ret) => console.log(ret)) //2秒后,注意这里返回的是数组 [2,3]
.catch( err => console.log(err.toString()));

Promise.race()

race意思是赛跑,看谁先到。只要p1, p2, p3中任意一个实例率先改变状态,则p的状态就跟着改变,而且状态由率先改变的实例决定。

let a = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(2) 
    }, 3000) 
  }) 

let b = new Promise((resolve, reject) => { 
    setTimeout(() => { 
      resolve(3) 
    }, 2000) 
  }) 

Promise.race([a, b])
.then( (ret) => console.log(ret)) //2秒后显示3
.catch( err => console.log(err.toString()));