ES6系列 (04):Promise对象


状态

Promise对象有三种状态:

  • pending: 进行中
  • fulfilled: 已成功
  • rejected: 已失败

resolve 函数:会由 pending-> fulfilled, 状态凝固不会再次改变

reject 函数:会由 pending-> rejected, 状态凝固不会再次改变

创建一个Promise实例

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供。

function getNumber(){
    const promise = new Promise((resolve, reject) => {
        //做一些异步操作
        setTimeout(function(){
            var num = Math.ceil(Math.random()*10); //ceil对num进行上舍入  生成1-10的随机数
            if(num<=5){
               return resolve(num);
            }
            else{
              return reject('数字太大了');
            }
        }, 2000);
    });
    return promise;            
}

调用resolvereject并不会终结 Promise 的参数函数的执行。如果不想执行后续代码,我们可以在resolvereject前面加上return语句。

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。

Promise常用方法

Promise.then()

then方法可以接受两个参数,第一个对应fulfilled状态的回调函数,第二个对应rejected状态的回调函数。第二个参数是可选的,不建议使用,最好使用catch方法

thencatch()方法返回的是一个Promise实例,存在以下三种情况

  • 当返回非Promise对象的值时,thencatch都生成一个状态为fulfilled的Promise对象,并把该返回值传入Promise链的下一环节
  • 当返回值为Promise对象时,生成的Promise对象的状态由被返回的Promise对象决定,传入Promise链下一环节的值也由这个被返回的Promise决定
  • 当Promise的回调函数中抛出错误时,thencatch都生成一个状态为rejected的Promise对象,并把抛出的错误对象传入Promise链的下一环节。
getNumber().then(
    function(data){
        console.log('resolved');
        console.log(data);
    }, 
    function(reason){
        console.log('rejected');
        console.log(reason);
    }
);

Promise.catch()

catch()方法效果和写在then的第二个参数里面一样,对应rejected状态的回调函数。不过它还有另外一个作用:在执行fulfilled状态的回调函数(也就是上面then中的第一个参数)时,如果抛出异常了,那么并不会报错卡死js,而是会进到catch方法中。

catch()方法返回的还是一个 Promise实例,因此后面还可以接着调用then()方法。

getNumber().then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义 ReferenceError: somedata is not defined
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

Promise.finally()

不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。

finally方法的回调函数不接受任何参数。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

Promise.all()

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

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

上面代码中,Promise.all()方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

p的状态由p1p2p3决定,分成两种情况。

  • 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

  • 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

注意:

Promise.all 方法会按照并行的模式,将所有请求一次性全部发送出去,然后等待接收到全部结果后,按照顺序打印出来。所以,该方法并不适用于继发执行请求

Promise.resolve()

Promise.resolve()方法会将现有对象转为 Promise 对象,该实例的状态为fulfilled

Promise.resolve('burc')
// 等同于
new Promise(resolve => resolve('burc'))

需要注意的是,立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve('burc').then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。

Promise.reject()

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

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

Promise.reject('出错了').catch(e => {
  console.log(e === '出错了')
})
// true

链式调用

Promise 是异步编程的一种解决方案,它的链式调用成功解决了传统的回调地狱难题,but,公司里的老代码用promise也写出了回调地狱,这很没有精神,下面是代码示例,论如何正确使用promise

promise错误用法 VS promise正确用法

参考文档

阮一峰 Promise对象

ES6