一. promise的基本概念和使用
promise根据《javascript高级程序设计》翻译为期约,代表的未来的结果
1.期约的状态
期约有三种状态
- 待定(pending)
- 兑现(fulfilled) 也称为解决状态
- 拒绝(reject)
期约状态是不可逆的,一旦决议为某一种状态将不会在修改为其他状态
2. 期约的声明和基本方法
promise是es6新增的引用类型,创建时需要传入一个执行器函数
let p = new Promise(() => {})
console.log(p) // 打印显示p的内部属性[[PromiseState]]属性为pending,表示待定状态
// 可以通过传入的执行器函数控制期约的状态
let p1 = new Promise((resolve, reject) => {
resolve() // 执行resolve回调,期约状态会变成兑现,fulfilled
reject() // 执行reject回调,会变成reject状态
})
console.log(p1);
执行器函数是同步执行的,因为执行器函数是Promise类的初始化程序,因为期约的不可逆性,所以resove和reject只能执行一个,一旦给出了期约结果,另一个将不再执行。
3. 通过静态方法直接实现解决的期约
- Promise.resolve()直接给出解决的期约
- Promise.reject()给出拒绝的期约 下面两个期约实例都是期约兑现的状态,reject也同理
let p1 = new Promise((resolve, reject) => resolve())
let p2 = Promise.resolve()
解决期约的值都是通过resolve的参数来传递,多余的参数被忽略
let P = Promise.resolve(123)
console.log(P); // Promise {<fulfilled>: 123}
Promise.resolve()具有幂等性,对一个值指定多次操作结果相同
let p = Promise.resolve(123)
console.log(p); // Promise {<fulfilled>: 123}
console.log(Promise.resolve(p)); // Promise {<fulfilled>: 123} // 与第一次执行结果相同
console.log(p === Promise.resolve(p)); // true
console.log(p === Promise.resolve(Promise.resolve(p))); // true
Promise.reject()有所不同,他不是幂等性,如果传入一个期约对象,不会把该期约对象的拒绝理由当做当前的拒绝理由,而是把传入期约本身当作拒绝理由
let p = Promise.reject(1)
console.log(p); // Promise {<rejected>: 1}
// 把上一个期约对象整个当做拒绝的理由
console.log(Promise.reject(p)); // Promise {<rejected>: Promise}
4. 期约的实例方法
- Promise.prototype.then() 为期约实例添加时间处理方法,接受俩个参数
- onResolved // 在期约为兑现时执行
- onRejected // 在期约拒绝
let p1 = Promise.resolve('resolve')
let p2 = Promise.reject('reject')
p1.then((res) => {
console.log(res); // resolve
}, (err) => {
console.log(err);
})
p2.then((res) => {
console.log(res);
}, (err) => {
console.log(err); // reject
})
Promise.prototype.then返回一个新的期约实例,新的期约实例基于then的onResolved处理程序构建,onResolve处理程序的返回值会通过Promise.resolve()包装生成新期约。如果没有显式返回值,则返回Promise.resolve()包装默认返回值undefined的新promise实例。如果没有传递处理程序,会包装上一个期约的解决之后的值,即直接返回按原样返回上一个期约,等同于返回上一个期约实例。
// 不传原样往后传,返回的期约实例,就是上一个期约实例
let p1 = Promise.resolve('foo')
let p2 = p1.then()
console.log(p2); // [[PromiseState]] fulfilled foo
// 没有显式返回值,用Promise.resolve包装默认返回值undefined,作为返回的新期约实例
let p1 = Promise.resolve('resolve')
let p2 = p1.then(() => {})
let p3 = p1.then(() => undefined)
let p4 = p1.then(() => Promise.resolve())
console.log(p2);// [[PromiseState]]: fulfilled, [[PromiseResult]]: undefined
console.log(p3);// [[PromiseState]]: fulfilled, [[PromiseResult]]: undefined
console.log(p4); // [[PromiseState]]: fulfilled, [[PromiseResult]]: undefined
// 如果有显式返回值,用Promise.resolve包装返回值,作为返回的新期约实例,
// 以下两个相同的,
// 第二个由于箭头函数,所以返回值本身时一个新的期约,
// 按照规则需要用Promise.resolve()包装返回值,
// 由于其幂等性,所以最终还是返回的最初的Promise.resolve返回的期约
let p1 = Promise.resolve('resolve')
let p2 = p1.then(() => 'bar')
let p3 = p1.then(() => Promise.resolve('bar'))
console.log('p2', p2); // [[PromiseState]] fulfilled [[PromiseResult]]: bar
console.log('p3', p3); // [[PromiseState]] fulfilled [[PromiseResult]]: bar
// 保留显示返回的期约,
let p = Promise.resolve()
let p1 = p.then(() => new Promise(() => {}))
let p2 = p.then(() => Promise.reject(1))
setTimeout(() => {
console.log(p1); //Promise <pending>
}, 0);
setTimeout(() => {
console.log(p2); // Promise<pending>
}, 0);
// 抛出异常会返回拒绝的期约
let p = Promise.resolve()
let p2 = p.then(() => {throw 'err'})
setTimeout(() => {
console.log(p2); // Promise<pending>
}, 0);
对于只传onRejected处理程序,和之前类似,按同样的规则用Promise.resolve()包装返回
5. Promise.prototype.catch
- 只接受拒绝处理程序相当于Promise.prototype.then(null, onRejected)
- 也返回一个新的期约实例,返回的期约实例与onRejected处理程序一样
6. Promise.prototype.finally
- 无论是解决或者拒绝状态都会执行
- 用途是减少两个处理程序中的冗余代码,
- 该方法不知道期约的状态
- finally也会返回一个新的期约 大多数情况下,finally不显示返回拒绝和待定的期约而且不出错的情况,会返回原样返回父期约,无轮父期约是三种状态中的什么状态
let p1 = Promise.resolve('foo')
let p2 = p1.finally()
let p3 = p1.finally(() => undefined)
// 都原样保留了父期约
console.log(p2); // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "foo"
console.log(p3); // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "foo"
如果finally处理程序的返回值是一个待定的期约,或者运行程序出了错误(显示的错误,或者返回一个拒绝的期约),则会返回对应的期约(待定或者拒绝)
let p1 = Promise.resolve()
let p2 = p1.finally(() => {console.log(a);}) // a为定义所以会抛出一个错误
let p3 = p1.finally(() => Promise.reject())
let p4 = p1.finally(() => new Promise(() => {}))
console.log(p2); // [[PromiseState]]: "rejected" [[PromiseResult]]: ReferenceError
console.log(p3); // [[PromiseState]]: "rejected" [[PromiseResult]]: undefined
console.log(p4); // [[PromiseState]]: "pending" [[PromiseResult]]: undefined
7. Promise.all
- 传递一个或者多个期约实例,只有在所有期约解决之后,再解决
let p = Promise.all([
Promise.resolve('foo'),
new Promise((resolve, reject) => setTimeout(() => {
resolve('delay foo')
}, 1000))
])
setTimeout(() => {
console.log(p); // Promise <pending>
}, 0);
// 1秒后,打印delay foo, 说明合成期约是在所有期约都解决之后才会解决
p.then((res) => console.log(res)) //
- 一个期约拒绝或者待定,导致最终是拒绝或者待定
- 只有都是解决,最终才是解决,合成期约的解决值是包含所有期约的解决值数组,
let p = Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
])
p.then(res => console.log(res)) // [1, 2, 3]
- 如果有期约拒绝,则最终拒绝的理由是第一个拒绝的期约
8. Promise.race()
返回一个期约,返回一组最先解决的期约镜像
易混点总结
- 对于then返回的期约实例,是根据onResolved处理程序构建的,通过Promise.resolve()包装其返回值作为期约实例返回,对于显示返回期约实例则保留显示返回的期约,抛出异常返回拒绝期约。如果没有传递,原样后传。不传onResolved处理程序是类似的。
- finally,返回一个新的期约实例,一般情况下,无论父期约是解决还是拒绝,都会原样后传。如果显式返回了一个待定的期约,或者返回一个拒绝的期约,就会返回对应的待定或者拒绝期约
参考文献《javascript高级程序设计》