Promise

517 阅读5分钟
  • promise简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise是一个对象,从它可以获取异步操作的消息.

一.特点

  • 1.对象的状态不受外界影响.Promise对象代表一个异步操作,有三种状态Pending(进行中),Resolved(已完成 又称Fulfiled)和Rejected(已失败).只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态.
  • 2.一旦状态改变就不会再变,任何时候都可以得到这个结果.Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected.只要这两种状态发生,状态就凝固了,不会再变了,会一直保持这个结果,如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果.这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的.

二.缺点

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

三.基本用法

Promise对象是一个构造函数,用来生成Promise实例

let promise1 = new Promise((resolve, reject) => {
    console.log('开始执行');
    resolve('success');
    reject('error');
});
promise1.then((value) => {
    console.log(value)
}, (err) => {
    console.log(err)
})
// 输出 开始执行  success
function timeout(ms) {
    return new Promise((resolve, reject) => {
        console.log('开始执行')
        setTimeout(resolve, ms, 'done');
    });
}

timeout(1000).then((value) => {
    console.log(value);
});
// 输出开始执行  1000毫秒后输出 done
  • 上面timeout方法返回一个promise,1000毫秒后执行resole方法
let p1 = new Promise((resolve, reject) => {
    console.log('p1开始执行');
    setTimeout(() => reject(new Error('fail')), 3000);
    console.log('p1结束?');
})
let p2 = new Promise((resolve, reject) => {
    console.log('p2开始执行');
    setTimeout(() => resolve(p1), 1000)
})
p2.then(data => {
    console.log(data)
}).catch(error => {
    console.log(error)
})
// 输出 p1开始执行 p1结束? p2开始执行 3000毫秒后输出 fail
  • 上面方法中 p2的then方法的返回值为promise的实例p1,将先执行p1,在p1中3000毫秒后reject一个error,p2的catch方法输出这个error

四.'冒泡'

Promise对象的错误具有'冒泡'性质,会一直向后传递,直到被捕获为止,也就是说错误总会被下一个catch语句捕获

// bad
promise.then(data => {}, err => {});
// good
promise.then(data => {}).catch(error => {})
  • 面代码中,第二种写法要优于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch).因此建议使用catch方法,而不是使用then方法的第二个参数.
  • 跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应 但是Chrome浏览器不遵守这条规定,它会抛出错误 "RfterenceError:"

五.几个小方法

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

let pall1 = new Promise((resolve, reject) => {
    resolve('resolve1');
    reject('reject1');
})
let pall2 = new Promise((resolve, reject) => {
    resolve('resolve2');
    reject('reject2');
})
let pall3 = new Promise((resolve, reject) => {
    resolve('resolve3');
    reject('reject3');
})
let p1 = Promise.all([pall1, pall2, pall3]);
  • 上面代码中,Promise.all方法接收一个数组作为参数, pall1,pall2,pall3都是Promise对象的实例,如果不是,就会调用Promise.resolve方法,将参数转为Promise实例,再进一步处理. p1的状态由p1,p2,p3决定分成两种情况
  • 只有pall1, pall2, pall3的状态都变成fulFiled, p1的状态才会变成fulFiled,此时p1,p2,p3的返回值组成一个数组,传递给怕的回调函数
  • 只要pall1, pall2, pall3之中的一个被rejected,p1的状态变成rejected,此时第一个被reject的实例的返回值,会传递给p1的回调函数

2)Promise.race() 方法将多个Promise实例,包装成一个新的Promise实例

let p = Promise.race([pall1, pall2, pall3]);
// 只要pall1, pall2, pall3之中的一个实例率先改变状态,p的状态就跟着改变,那个率先改变的Promise实例的返回值,就传递给p的实例,再进一步处理

3)Promise.resolve() 将现有对象转为Promise对象

  1. 参数是一个Promise实例,那么Promise.resolve将不做任何修改,原封不动的返回这个实例
  2. 参数是一个thenable对象 thenable对象指的是具有then方法的对象
let thenable = {
    then: (resolve, reject) => {
        resolve(11);
    }
}
// Promise.resolve方法会将这个对象转为Promise对象, 然后立即执行thenable对象的then方法
let pResoleve = Promise.resolve(thenable);
pResoleve.then(data => {
    console.log(data)
})
  • 上面代码中,thenable对象的then方法执行后,对象pResolve的状态就变为resolved从而立即执行最后那个then方法指定的回调函数 输出11
  1. 参数不是具有then方法的对象,或根本就不是对象
let p4 = Promise.resolve('hello');
p4.then(data => {
    console.log(data)
})
  • 如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为Resolved
  • 上面代码生成一个新的Promise对象的实例p4,由于字符串不属于异步操作(判断方法是它不具有then方法的对象),返回promise状态从一生成就是Resolved,所以回调函数会立即执行,Promise.resolve方法的参数,会同时传给回调函数
  1. 不带任何参数
  • Promise.resolve方法允许调用时不带参数,直接返回一个Resolved状态的Promise对象

4) Promise.reject()

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

5) done()

  • p1.then().done()
  • Promise方法的回调链,不管是以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到,可以用done()方法,总是处于回调链的尾端,保证抛出任何可能出现的错误

6)finally()

  • p2.then().done().finally()
  • finally()方法用于指定不管Promise对象最后状态如何,都会执行的操作,它与done方法最大的区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行