从心认识ES6|Promise对象

一、什么是Promise

Promise是一个优秀的异步编程解决方案,它是一个对象,用来获取异步操作的消息。Promise具有两个重要的特点:

1.对象的状态不受外界的影响,有三种状态分别是:pending(进行中),fulfilled(已成功),rejected(已失败),只有异步操作的结果才能改变其状态。
2.一旦状态改变,之后就不会再改变。状态改变只有pending->fulfilledpending->rejected两种。一旦改变发生状态就凝固,即使再添加回调函数也是没有用了。

Promise也是有缺点的,主要是以下几点:

1.无法取消Promise,一旦新建它就会立即执行,无法中途取消。
2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
3.当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

二、基本用法

创造Promise实例:

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){	//由JS引擎提供,不用自己部署,就是不用自己声明
    resolve(value);
  } else {
    reject(error);
  }
});

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

一个简单的Promise对象实例:

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});
promise.then(function() {
  console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved

promise实例在被创建时就是立即执行,所以先输出Promise,而.then是promise执行完的回调,所以后输出resolved。

Promise实现多重异步操作:

在声明的时候,Promise传递的参数函数会立即执行,因此Promise使用的正确姿势是在其外层再包裹一层函数。在只有一重的异步操作时,Promise似乎没什么优势,而在多重的异步实现中就大不一样了。下面是一个例子中,如果没有采用Promise,回调地狱是不可避免的,而使用Promise就可以完美的管理几个相互依赖的函数的顺序。

//第一个异步任务
function run_a(){
    return new Promise(function(resolve, reject){
        //假设已经进行了异步操作,并且获得了数据
        resolve("step1");
    });
}
//第二个异步任务
function run_b(data_a){
    return new Promise(function(resolve, reject){
        //假设已经进行了异步操作,并且获得了数据
        console.log(data_a);
        resolve("step2");
    });
}
//第三个异步任务
function run_c(data_b){
    return new Promise(function(resolve, reject){
        //假设已经进行了异步操作,并且获得了数据
        console.log(data_b);
        resolve("step3");
    });
}

//连续调用
run_a().then(function(data){
    return run_b(data);
}).then(function(data){
    return run_c(data);
}).then(function(data){
    console.log(data);
});

/*运行结果
  step1
  step2
  step3
*/

Promise实现链式调用的流程图如下

从心认识ES6|Promise对象

三、Promise.prototype.then()

其实在上面我们已经介绍了Promise的then方法,这是定义在Promise.prototype原型上的方法。then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function funcA(comments) {
  console.log("resolved: ", comments);
}, function funcB(err){
  console.log("rejected: ", err);
});

上面的例子中,getJSON是一个采用Promise封装的方法,第一个then返回一个新的Promise对象,第二个then只有等到这个新的Promise状态发生改变才会执行。

四、Promise.prototype.catch()

和then方法一样,catch也是Promise.prototype原型上的方法,用于指定发生错误时的回调函数。如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。

getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 处理前面三个Promise产生的错误
});

在一般情况下,虽然then方法可以接受两个回调,分别对应fulfilled和rejected状态,但是更为普遍的是采用catch方法来处理rejected对应的处理。所以上面的例子中,任何的Promise抛出的错误都会被最后的catch捕获。

五、Promise.prototype.finally()

finally也是定义在Promise.prototype原型上的方法,同样返回新的Promise对象,不同的是,无论Promise的状态是变成fulfilled还是rejected,finally都会被执行。这也体现了finally方法是不能知道Promise对象的状态的。

server.listen(port)
  .then(function () {
    // ...
  })
  .catch(function(){
	// ...
  })
  .finally(server.stop);

这个例子中,无论前面是执行了then还是catch,都会执行后面的finally。

六、Promise.all()

Promise.all是将很多个Promise对象包装成一个Promise对象。

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

例子中,只有p1, p2, p3状态都变成fulfilled,p的状态才会变成fulfilled并接受p1, p2, p3回调组成的数组;否则p的状态就是rejected并接受第一个rejected的回调。

七、Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

例子中,如果p1, p2, p3中有一个状态先改变,p的状态就跟着改变,而且先改变的Promise回调会传给p。

八、Promise.resolve()

用于将对象转化成Promise对象,根据传入的参数不同,执行不同的操作。
参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

参数是一个thenable对象
thenable对象指的是具有then方法的对象。Promise.resolve()会立即执行thenable里的then,状态变为resolved。

参数不是具有then方法的对象,或根本就不是对象
Promise.resolve方法返回一个新的Promise对象,状态为resolved。

不带有任何参数
直接返回一个Promise对象,状态为resolved。

九、Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

参考:
阮一峰ES6
MDN Promise
Promise简单入门

微信扫一扫,关注“厦猿”,获取更多前端学习资料
从心认识ES6|Promise对象