为了加深手写 promise 代码的理解,会记录各个情况下promise代码执行顺序,和为什么要这么写。
首先贴上完整的手写Promise代码:(尚硅谷课程代码)
class Promise {
// 构造方法
constructor(executor) {
this.status = 'preding' // 给promise对象指定status属性,初始值为prending
this.data = undefined //给promise对象指定一个用于存储结果数据的属性
const self = this // 保存实例对象的this的值
this.callbacks = []// 每一个元素的结构:{onResolved(){},onRejected() {}}
// Promise实例对象的resolve
// 返回一个指定成功的promise
function resolve(value) {
// 如果当前状态不是Prending,直接结束
if (self.status != 'preding') {
return
}
//改变状态
self.status = 'resolved'
//保存异步操作得到的value数据
self.data = value
// 执行onResolve回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(value)
})
})
}
// Promise实例对象的reject(定义在Promise的原型上面)
// 返回一个指定失败的promise
function reject(reason) {
// 如果当前状态不是Prending,直接结束
if (self.status != 'preding') {
return
}
//改变状态
self.status = 'rejected'
//保存value数据
self.data = reason
// 执行onRejected回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(reason)
})
})
}
// 执行回调函数
try {
// 同步调用 [执行器函数]
executor(resolve, reject)
} catch (error) {
// 修改promise对象状态为失败
return reject(error)
}
}
// then方法封装
then(onResolved, onRejected) {
const self = this
// 判断回调函数参数
if (typeof onRejected != 'function') {
onRejected = reason => {
throw reason;
}
} // 考虑异常穿透,没有指定reject,初始化reject为异常
if (typeof onResolved != 'function') {
onResolved = value => value
} // 默认省略resolve,reject回调,初始化resolve,then也可以继续往下传
return new Promise((resolve, reject) => {
// 封装函数
function callBack(onFun) {
try {
// 获取回调函数执行结果
let result = onFun(self.data)
//判断 结果是否是Promise对象
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {//不是直接成功
//状态为成功
resolve(result)
}
} catch (error) {
reject(error)
}
}
// 调用回调函数
if (this.status === 'resolved') {
setTimeout(() => {
callBack(onResolved)
})
}
if (this.status === 'rejected') {
setTimeout(() => {
callBack(onRejected)
})
}
// 改变状态前,绑定回调函数
if (this.status === 'preding') {
// 保存回调函数
this.callbacks.push({
onResolved: () => callBack(onResolved),
onRejected: () => callBack(onRejected)
})
}
})
}
在各个场景下手写Promise的执行顺序:
同步任务下,then方法返回promise对象
let p = new Promise((resolve, reject) => {
resolve('ok')
});
const result = p.then(value => {
//1.返回字符串
// return `hello promise`
// 2.返回promise
// return new Promise((resolve, reject) => {
// resolve("success")
// })
// 3.抛出异常
throw '123'
}, reason => {
console.log(reason)
})
console.log(result)
在内置的promise下:
在手写的promise下:
分析:
两者是正确的,由于then方法的回调函数是异步执行的,所以console.log(result)会在先打印出来,所以promise会显示为preding,但当所以同步事件执行完后,会执行异步操作,所以
value => {throw ‘123’} 会执行,使得result的状态变成了rejected。
代码分析:
对构造函数来说,
let p = new Promise((resolve, reject) => {
resolve('ok')
});
首先会对promise进行初始化包括:状态、结果数据、以及then绑定回调函数容器(callbacks)
初始化完成后,会调用执行函数,对执行函数中绑定的回调(resolve,reject)进行调用。
从而修改promise对象的状态(状态只能改变一次)和绑定值(这里的’ok’) 可能执行then方法的回调,这里没有then,就不执行;最后这句就返回 resolved状态,data为’ok’的promise对象。
constructor(executor) {
this.status = 'preding' // 给promise对象指定status属性,初始值为prending
this.data = undefined //给promise对象指定一个用于存储结果数据的属性
const self = this // 保存实例对象的this的值
this.callbacks = []// 每一个元素的结构:{onResolved(){},onRejected() {}}
// Promise实例对象的resolve
// 返回一个指定成功的promise
function resolve(value) {
// 如果当前状态不是Prending,直接结束
if (self.status != 'preding') {
return
}
//改变状态
self.status = 'resolved'
//保存异步操作得到的value数据
self.data = value
// 执行onResolve回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(value)
})
})
}
// Promise实例对象的reject(定义在Promise的原型上面)
// 返回一个指定失败的promise
function reject(reason) {
// 如果当前状态不是Prending,直接结束
if (self.status != 'preding') {
return
}
//改变状态
self.status = 'rejected'
//保存value数据
self.data = reason
// 执行onRejected回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(reason)
})
})
}
// 执行回调函数
try {
// 同步调用 [执行器函数]
executor(resolve, reject)
} catch (error) {
// 修改promise对象状态为失败
return reject(error)
}
}
下面这段代码: 调用then方法,(then方法总是返回一个promise对象,这是支持链式调用的原因)then 方法是整个promise的核心部分。then方法返回的promise对象状态由then方法里面回调函数执行结果决定,这里throw ’123‘, 所以 result的状态肯定是reject。
1.由于then方法可以省略写成功回调和失败的回调,所以需要在不传回调函数时,给它们默认值。
2.然后是返回新的promise对象,同样是调用构造函数,初始化状态为(predding),然后执行构造函数中的executor(执行器函数)调用回调函数,这里的回调函数是then方法创建promise时,传入的回调函数,所以又返回到then方法promise传入的回调函数中,
3.由于这时this.status的状态是resolve,(this是这里p)所以执行then方法成功的回调,由于then中的回调函数是异步执行的,所以才在这里加了setTimeOut
4.异步调用()=>{throw ’123’}后,被catch捕获异常,直接调用promise对象的,reject方法。 改变了新创建的promise对象的状态,为(reject,同时data也赋值为‘123’)完毕。
这里好像没有调用this.callbacks.push()来绑定then的回调函数 ,那是因为是同步任务下,调用了成功的回调resolve, 将 p的状态变成了resolved。如果是promise是异步任务下呢?
const result = p.then(value => {
//1.返回字符串
// return `hello promise`
// 2.返回promise
// return new Promise((resolve, reject) => {
// resolve("success")
// })
// 3.抛出异常
throw '123'
}, reason => {
console.log(reason)
})
// then方法封装
then(onResolved, onRejected) {
const self = this
// 判断回调函数参数
if (typeof onRejected != 'function') {
onRejected = reason => {
throw reason;
}
} // 考虑异常穿透,没有指定reject,初始化reject为异常
if (typeof onResolved != 'function') {
onResolved = value => value
} // 默认省略resolve,reject回调,初始化resolve,then也可以继续往下传
return new Promise((resolve, reject) => {
// 封装函数
function callBack(onFun) {
try {
// 获取回调函数执行结果
let result = onFun(self.data)
//判断 结果是否是Promise对象
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {//不是直接成功
//状态为成功
resolve(result)
}
} catch (error) {
reject(error)
}
}
// 调用回调函数
if (this.status === 'resolved') {
setTimeout(() => {
callBack(onResolved)
})
}
if (this.status === 'rejected') {
setTimeout(() => {
callBack(onRejected)
})
}
// 改变状态前,绑定回调函数
if (this.status === 'preding') {
// 保存回调函数
this.callbacks.push({
onResolved: () => callBack(onResolved),
onRejected: () => callBack(onRejected)
})
}
})
}
异步任务下,then方法返回promise对象
如果是promise是异步任务下呢?(绝大多少情况下,promise都是封装异步的任务),由于同步任务执行完,才执行异步任务。所以 下面这种情况下:
p.then(…)时 p的状态是predding,
- p是predding方法,调用then方法时,会绑定then方法里的回调函数。
- 当同步任务都执行完之后,会调用异步任务,setTimeout方法,将p的状态变成resolve
同时,将1中绑定then方法回调函数callBack(onResolved)进行执行, 将rs状态变成resolve, data= ‘hello promise’
self.callbacks.forEach(item => {
item.onRejected(reason)
})
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 1000)
// reject('on no')
});
const rs = p.then(value => {
//1.返回字符串
return `hello promise`
// 2.返回promise
// return new Promise((resolve, reject) => {
// reject("unsuccess")
//})
// 3.抛出异常
// throw '123'
}, reason => {
// throw '123'
})
console.log(rs)
内置的promise
手写的promise: