promise
之前有看过极客大学上的一个视频,那位老师提到了关于学习的追溯法,什么叫追溯法呢,就是你关于一个知识点的学习历程的链路。
关于我学promise的时候:
首先我先看了视频大致了解了一下
然后看了红宝书上关于这一部分的内容
然后看了一些技术博客
然后自己总结一篇博客。
常见的面试题
- promise解决了什么问题
- promise常用的API都有哪些?
- 手写一个符合Promise/A+规范的Promise
- Promise有什么缺陷,我们该如何解决?
概念
什么是Promise?
Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。
let p=new Promise(()=>{});
setTimeout(console.log,0,p)//Promise<pending>
复制代码
Promise的三种状态
- 待定(pending)
- 兑现 (resolved)
- 拒绝 (rejected)
- 待定是最初始状态,从待定转换为兑现或者拒绝,期约的状态就不在改变
- 期约的状态不受外界影响,是私有的。Promise对象代表一个异步操作,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
promise解决了什么问题
异步的行为是js的基础,以前的实现并不理想。早期js只支持定义回调函数来表明异步操作完成。串联多个异步操作是一个常见的问题。通常需要深度嵌套回调函数(俗称“回调地狱”)。
Promise解决了这个问题,因为他可以链式调用
- 消灭嵌套调用:通过 Promise 的链式调用可以解决;
- 合并多个任务的请求结果:Promise.all() Promise.race()
- promise常用的API都有哪些?
- Promise.resolve()
- Promise.reject()
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
Promise.resolve()的实现
下面两个期约实例实际上是一样的
let p1=new Promise((resolve,reject)=>resolve())
let p2=Promise.resolve()
默认产生一个成功的 promise。
static resolve(data){
new Promise((resolve,reject)=>resolve(data))
}
复制代码
这里需要注意的是,promise.resolve 是具备等待功能的。如果参数是 promise 会等待这个 promise 解析完毕,在向下执行,所以这里需要在 resolve 方法中做一个小小的处理:
Promise.reject()的实现
下面两个期约实例实际上是一样的
let p1=new Promise((resolve,reject)=>reject())
let p2=Promise.reject()
默认产生一个失败的 promise,Promise.reject 是直接将值变成错误结果。可以称作为理由。
static reject(reason){
new Promise((resolve,reject)=>reject(reason))
}
复制代码
Promise.prototype.then的实现
then方法最多接受两个参数:onResolved和onRejected处理程序。这两个参数都是可选的,他们会分别在期约分别进入“兑现”和“拒绝”状态时执行
Promise.prototype.catch的实现
事实上这个方法是一个语法糖,他相当于调用Promise.prototype.then(null,onRejected)
Promise.prototype.catch=function(errCallback){
return this.then(null,errCallback)
}
复制代码
Promise.prototype.finally的实现
finally 表示不是最终的意思,而是无论如何都会执行的意思。 他不能检测到期约的状态是解决还是拒绝。他只会表现为父期约的传递
Promise.prototype.finally=function(callback){
return this.then((value)=>{
return Promise.resolve(callback).then(()=>value)
},(reason)=>{
return Promise.reject(callback).then(()=>throw reason)
})
}
复制代码
测试一下:
Promise.resolve(456).finally(()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(123)
}, 3000);
})
}).then(data=>{
console.log(data,'success')
}).catch(err=>{
console.log(err,'error')
})
复制代码
控制台等待 3s 后输出: "456 success"
Promise.all()和Promise.race()
它们两个都是将多个期约实例组合成一个期约的方法
Promise.all()
这个静态方法创建的期约会在一组期约全部解决之后在解决。这个静态方法接收一个可迭代对象,返回一个新期约。
let p1 =Promise.all([
Promise.resolve(),
Promise.resolve(),
])
//可迭代对象中的元素会通过Promise.resolve()转换为期约
let p2=Promise.all([1,2])
//空的可迭代对象等价于Promise.resolve()
let p3=Promise.all([])
//无效语法
let p4=Promise.all()
复制代码
合成的期约只会在每个包含的期约都解决之后才解决
let p=Promise.all([
Promise.resolve(),
new Promise(
(resolve,reject)=>setTimeout(resolve,1000)
)
])
setTimeout(console.log,0,p)
p.then(()=>setTimeout(console.log,0,"打印成功"))
复制代码
//Promise
//打印成功 1s后
如果至少有一个包含的期约待定,则合成的期约也会待定。如果有一个包含的期约拒绝,则合成的期约也会拒绝。
如果所有的期约都成功解决,则合成期约的解决值就是所有包含期约解决值的数组,按照迭代组顺序:
let p=Promise.all([
Promise.resolve(3),
Promise.resolve(),
Promise.resolve(4)
])
p.then((values)=>setTimeout(console.log,0,values));
复制代码
//[3, undefined, 4]
如果有期约拒绝,则第一个拒绝的期约会将自己的理由作为合成期约的拒绝理由。之后再拒绝的期约不会影响最终期约的拒绝理由。
Promise.race()
这个静态方法返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像。这个方法接收一个可迭代对象,返回一个新期约。
Promise.race()不会对解决或拒绝的期约区别对待。无论是解决还是拒绝,只要是第一个落定的期约,Promise.race()就会包装其解决值或拒绝理由并返回新期约:
let p1=Promise.race([
Promise.resolve(3)
,new Promise((resolve,reject)=>setTimeout(reject,1000))
])
setTimeout(console.log,0,p1)//Promise<resolved>:3
复制代码
迭代顺序决定落定顺序
并不影响包含期约正常的拒绝操作。合成的期约会静默处理所有包含期约正常的拒绝操作。
手写一个符合Promise/A+规范的Promise
因为篇幅过长我想在单独的博客中单独总结这个部分。