Promise
Promise 是每一位前端都需要熟练掌握甚至精通的 api,手写能极大地增强我们的理解
首先我们准备一个类
class Promise2{
constructor(executor) {
this.status='pending'
const resolve=()=>{
if(this.status==='pending'){
this.status='fulfilled'
}
}
const reject=()=>{
if(this.status==='pending'){
this.status='rejected'
}
}
executor(resolve,reject)
}
}
现在的 Promise 可以执行 executor 并传入方法,接下来添加 then 方法
class Promise2{
constructor(executor) {
this.status='pending'
this.resolveResult=null
this.rejectResult=null
const resolve=(data)=>{
if(this.status==='pending'){
this.status='fulfilled'
this.resolveResult=data
}
}
const reject=(reason)=>{
if(this.status==='pending'){
this.status='rejected'
this.rejectResult=reason
}
}
executor(resolve,reject)
}
then(onResolved,onRejected){
if(this.status==='fulfilled'){
onResolved(this.resolveResult)
}
if(this.status==='rejected'){
onRejected(this.rejectResult)
}
}
}
我们添加属性来记录 resolve 或者 reject 传入的值,作为传入 then 的方法的参数,来测验一下吧
const p1=new Promise2((resolve,reject)=>{
console.log('start')
resolve('second')
})
p1.then(data=>console.log(data),reason=>console.log(reason))
可以看到,执行器函数可以执行,p1 的状态变为 fulfilled,同时记录了 resolve 的值,同时 then 的回调也能打印出 resolve 的值
看起来没问题,我们改为异步任务试一下
const p1=new Promise2((resolve,reject)=>{
console.log('start')
setTimeout(()=>{
resolve('second')
},1000)
})
p1.then(data=>console.log(data),reason=>console.log(reason))
结果如下
我们发现,并没有执行 then 的回调,原因是代码执行到 then 时,p1 的状态依然是 pending,所以 then 不进行任何处理,我们需要处理在 pending 状态的 promise
class Promise2{
constructor(executor) {
this.status='pending'
this.resolveResult=null
this.rejectResult=null
// 储存回调,待 resolve 时调用
this.callbacks=[]
const resolve=(data)=>{
if(this.status==='pending'){
this.status='fulfilled'
this.resolveResult=data
// 处理异步的 resolve
this.callbacks.forEach(item=>item.onResolved(data))
}
}
const reject=(reason)=>{
if(this.status==='pending'){
this.status='rejected'
this.rejectResult=reason
//处理异步的 reject
this.callbacks.forEach(item=>item.onRejected(reason))
}
}
executor(resolve,reject)
}
then(onResolved,onRejected){
if(this.status==='fulfilled'){
onResolved(this.resolveResult)
}
if(this.status==='rejected'){
onRejected(this.rejectResult)
}
if(this.status==='pending'){
this.callbacks.push({onResolved,onRejected})
}
}
}
测试结果如下
一秒后成功地打印出了 resolve 的值
不过还远没有结束,我们都知道 then 可以链式调用,因为 then 每次都返回一个 promise
每次返回的 promise 的状态取决于回调函数的返回值,若回调返回非 promise,则状态为 resolve
若返回值为 promise,则以此 promise 的状态为返回 promise 的状态,代码如下
then(onResolved,onRejected){
return new Promise2((resolve,reject)=>{
if(this.status==='fulfilled'){
const result= onResolved(this.resolveResult)
if(result instanceof Promise2){
result.then(data=>resolve(data),reason=>reject(reason))
}else{
resolve(result)
}
}
if(this.status==='rejected'){
const result= onRejected(this.resolveResult)
if(result instanceof Promise2){
result.then(data=>resolve(data),reason=>reject(reason))
}else{
resolve(result)
}
}
if(this.status==='pending'){
this.callbacks.push({onResolved,onRejected})
}
})
}
无论执行哪个回调,都要判断回调的返回值,如果 result 为 promise,则用 then 来监视 result 内部的状态,如果 result 内部 resolve,则 result 的状态为 fulfilled,则一定会执行 resolve(data),然后 then 返回的 promise 的状态变为 fulfilled,而且记录了 result 内部 resolve 传出的值,便于再次 then 时回调能够取到对应的值
接下来补全剩下的状态
then(onResolved, onRejected) {
return new Promise2((resolve, reject) => {
if (this.status === 'fulfilled') {
const result = onResolved(this.resolveResult)
if (result instanceof Promise2) {
result.then(data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
}
if (this.status === 'rejected') {
const result = onRejected(this.rejectResult)
if (result instanceof Promise2) {
result.then(data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
}
if (this.status === 'pending') {
this.callbacks.push({
onResolved: () => {
const result = onResolved(this.resolveResult)
if (result instanceof Promise2) {
result.then(
data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
},
onRejected: () => {
const result = onResolved(this.rejectResult)
if (result instanceof Promise2) {
result.then(
data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
}
})
}
})
}
对于 pending 状态的处理,其实异步的执行和同步无异,我们只需要先保存一会要做的事情,即检查返回值,并根据 result 内部的处理来决定 then 返回的 promise 的状态, 测试一下
new Promise2((resolve, reject) => {
console.log('start')
setTimeout(() => {
resolve('second')
}, 1000)
}).then(data => {
console.log(data)
return new Promise2((resolve, reject) => {
setTimeout(()=>{
reject('error')
},1000)
})
}
, reason => console.log(reason))
.then(data => console.log(data), reason => console.log(reason))
可以看到,结果被成功打印出
其实 promise 的工作原理就是值的传递和状态的传递,一旦 resolve 或 reject 执行,值就被记录下来,状态也被改变,在 then 中,如果执行 resolve 的回调,先观察返回值的成功和失败情况,若成功,调用要返回的 promise 的 resolve,其状态被改变,而且值由内部的 resolve 传出,此时状态的值都已经就位,便可以开始无尽的 then 啦
最后贴上完整代码
class Promise2 {
constructor(executor) {
this.status = 'pending'
this.resolveResult = null
this.rejectResult = null
this.callbacks = []
const resolve = (data) => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.resolveResult = data
this.callbacks.forEach(item => item.onResolved(data))
}
}
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.rejectResult = reason
this.callbacks.forEach(item => item.onRejected(reason))
}
}
executor(resolve, reject)
}
then(onResolved, onRejected) {
return new Promise2((resolve, reject) => {
if (this.status === 'fulfilled') {
const result = onResolved(this.resolveResult)
if (result instanceof Promise2) {
result.then(
data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
}
if (this.status === 'rejected') {
const result = onRejected(this.rejectResult)
if (result instanceof Promise2) {
result.then(
data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
}
if (this.status === 'pending') {
this.callbacks.push({
onResolved: () => {
const result = onResolved(this.resolveResult)
if (result instanceof Promise2) {
result.then(
data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
},
onRejected: () => {
const result = onResolved(this.rejectResult)
if (result instanceof Promise2) {
result.then(
data => resolve(data),
reason => reject(reason))
} else {
resolve(result)
}
}
})
}
})
}
}