Promise
Promise是ES6新增的一个对象,代表了即将要发生的事情,是在处理异步过程中传达信息的一种方式,
它有哪些特性呢?可以去看看我以前写过的同步与异步 Promise这篇文章。但是 首先我们需要知道,它的状态不受外界的影响,而Promise里有三个状态
- Pending: 等待状态,可以转换成resolve成功态,和reject拒绝态
- Resolve: 成功态,表示操作成功,可以抛出相应的内容
- Reject: 拒绝态,表示操作失败,也可以抛出相应的报错信息
那么我们就根据Promise的功能,自己写一个Promsie对象
我们先定义Promise对象的三个状态,这叫做常量管理,使用大写字母替代
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
我们知道Promise是一个对象,且可以被关键字new使用,那么Promise就作为一个构造函数使用,其中具有resolve回调方法,reject回调方法,并且转换为成功态或者拒绝态后会抛出相应的回调,给与.then方法使用,而回调可能不止一个,所以我们采用两个数组来装载相应的回调,所以不难写出以下代码框架。
function MyPromise(fn) {
const that = this //绑定this为调用该Promise的对象的作用域,获取正确的this对象
this.value = null //resolve或者reject的参数
that.resolvedCallbacks = [] // 存放then里面的回调函数1
that.rejectedCallbacks = [] // catch里面的回调函数2
this.state = PENDING
function resolve(value) {
}
function reject(value) {
}
fn(resolve, reject)
}
往MyPromise的原型上挂上.then方法,接收回调,成功态触发onFullfilled回调,拒绝态触发onRejected回调,并执行
MyPromise.prototype.then = function(onFulfilled, onRejected) {
}
我们在搭好需要的框架后,慢慢地编写细节。首先,resolve,reject的状态是不允许逆向修改的,只能由pengding态转变为resolve或reject态,所以在此之前需要判定当前状态是否为Pending状态,若为其他的状态,则不能转换执行相应的回调。否则就该执行相应的回调,同时考虑到可能会有多个回调,所以我们需要遍历相应的回调数组,并执行,
同时fn的执行可能成功,可能失败,严谨性考虑,采用try catch 语句
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value //获取到该参数
that.resolvedCallbacks.map(cb => cb(that.value))//可能接收多个回调
}
}
function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
try { //调用成功
fn(resolve, reject)
} catch (error) {
reject(error)
}
之后我们编写.then方法,首先.then方法当中的参数是否是个函数,需要进行判定,防止后续方法出现问题。状态变更的时候,相应的状态发生改变,.then函数需要马上执行,如果还是pending状态,则需要将回调压进相应状态的数组,如果状态已经发生变更,则执行相应状态下的方法
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
const promise2 = null
//传来的参数必须是个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {
throw r
}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
但是如果遇到下列代码
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
},1000)
}).then(value => {
console.log(value);
})
定时器当中调用resolve回调,而由于setTimeout异步在event-loop执行顺序当中是在异步任务当中的宏任务,.then函数是微任务,所以先执行,而在此时状态迟迟没有变更,那么就不能够调用相应的回调并且触发.then里的回调,所以需要先判断pengding状态,将相应的回调装进回调数组起来,等到状态变更之后再次触发。
调用
那么写到这里,已经是小小的一步成功,我们采用以下方法来调用自己写的MyPromise方法,先从pengding态转换为拒绝态,之后再利用定时器,看看是否能再次转换为成功态,来表明我们自己写的MyPromise是否成功
const p = new MyPromise((resolve, reject) => {
reject('no')
setTimeout(() => {
resolve('yes')
}, 1000)
})
p.then(res => {
console.log(res);
},
err => {
console.log(err); //no
})
**结果证明,我们写的代码是正确的,但是这只是精简版本的,所有代码如下:但是对于想要入大厂的你们来说,远远不够。
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this
that.state = PENDING
that.value = null // 存放resolve()内部的参数
that.resolvedCallbacks = [] // then里面的回调函数1
that.rejectedCallbacks = [] // then里面的回调函数1
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
that.resolvedCallbacks.map(cb => cb(that.value))
}
}
function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
try {
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {
throw r
}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
进阶版
我们都知道Promise同样可以返回一个对象,进行链式调用.then方法。所以我们需要加以判定value是否也是一个MyPromise对象,如果是,我们就直接在里面调用.then方法,传进相应的回调函数参数,而为了保证正确的函数执行顺序,所以我们需要用定时器将后续代码包裹起来
function MyPromise(fn) {
const that = this //防止作用域被修改,获取正确的this对象
that.state = PENDING
that.value = null // 存放resolve()内部的参数 value作为中转站 与then相连
that.resolvedCallbacks = [] // 存放then里面的回调函数1
that.rejectedCallbacks = [] // catch里面的回调函数2
function resolve(value) {
//value是否为MyPromise这个类型的实例对象 .then后面调用.then
if (value instanceof MyPromise) {
return value.then(resolve, reject)
}
//去到下次的任务队列 保证then的执行顺序
setTimeout(() => { //状态发生变更就要回调
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value //获取到该参数 可能接收多个回调
that.resolvedCallbacks.map(cb => cb(that.value))
}
}, 0)
}
function reject(value) {
setTimeout(() => {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}, 0)
}
try { //调用成功
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
之后我们更改.then函数当中的内容,我们定义一个promise2对象,表示返回的一个新的promsie对象,如果状态还是pengding,需要先保存新的promise对象,并且push相应的回调函数,执行之后返回一个x,为了不同的promise兼容使用,创建resolutionProcedure新函数,将新的执行结果x和promise2对象传入,抛出给.then函数使用,等待状态变更之后再执行。而为了代码严谨性,也要用try catch包裹。。
//原型挂上then方法 接收回调resolve reject
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
const promise2 = null
//传来的参数必须是个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {
throw r
}
//判断当前的状态
if (that.state === PENDING) {
return (promise2 = new MyPromise((resolve, reject) => {
that.resolvedCallbacks.push(() => {
try {
const x = onFulfilled(that.value)
resolutionProcedure(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
that.rejectedCallbacks.push(() => {
try {
//下个promise的回调 x为 第一层.then的执行结果
const x = onRejected(that.value)
//放置第二层执行.then
resolutionProcedure(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}))
// that.resolvedCallbacks.push(onFulfilled)
// that.rejectedCallbacks.push(onRejected)
}
//状态已经变更 就调用方法
if (that.state === RESOLVED) {
//返回一个新的promise对象
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
//下个promise的回调 x为 第一层.then的执行结果
const x = onFulfilled(that.value)
//放置第二层执行.then
resolutionProcedure(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}))
// onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
// ...
}
}
我们在resolutionProcedure函数当中,兼容使用多种不同的promise,需要判断当前的promise和上一次.then执行抛出的promise对象是否相同,相同就不予执行,抛出类型错误,避免发生循环引用的问题。就比如下面的代码,p对象的.then函数执行后,抛出了p1对象,也就是x,若是与当前promise相同,那么p1再次调用自身,就会反复执行p1,进入循环,所以需要判定
let p = new Promise((resolve, reject) => {
resolve(1)
})
let p1 = p.then(value => {
return p1
}}
之后我们还需要判断x的类型,x是MyPromise对象,才可以继续抛出promise对象,并接收相应的回调,继续执行下一个.then函数,而下一个.then接收的是上一个.then抛出来的值,这是我们需要注意的。
若是遇到这种调用MyPromise的代码,也是被允许的,所以我们需要完善。
.then(value => {
return new Promise().then(() => {
})
})
为了应对上述代码的调用,我们有了应对之策,上述代码当中抛出的promise对象就是x。而上一次.then方法的执行结果x不为空,且x为对象或者方法,那么需要获取到x的.then,如果也是一个方法,那么就执行,同时利用called这个标记,判断是否已经调用过,确保回调只会执行一次.此时需要再次判断抛出的是否也为函数,如果是,抛出resolutionProcedure(promise2, x, resolve, reject)继续进行;不是函数,那么直接resolve(x),抛出内容。
function resolutionProcedure(promise2, x, resolve, reject) {
//前一个.then 和后一个 .then接收的对象不能一样
if (promise2 === x) { //不能抛出同一个promise对象
return reject(new TypeError('Error'))
}
if (x instanceof MyPromise) { //仍然是一个promise对象,继续抛出该对象,给下一个.then
x.then(function(value) {
resolutionProcedure(promise2, value, resolve, reject)
})
}
let called = false //控制执行回调仅为一次
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then////考虑可能会return new Promise.then({}),获取x的then方法
if (typeof then === 'function') {
then.call(
x,
y => { //只执行一次回调
if (called) return
called = true
resolutionProcedure(promise2, y, resolve, reject)
},
e => {
if (called) return
called = true
reject(e)
}
)
} else { //x不是函数
resolve(x) //说明不是promise对象,直接resolve
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
resolve(x)
}
}
完整规范代码
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this //防止作用域被修改,获取正确的this对象
that.state = PENDING
that.value = null // 存放resolve()内部的参数 value作为中转站 与then相连
that.resolvedCallbacks = [] // 存放then里面的回调函数1
that.rejectedCallbacks = [] // catch里面的回调函数2
function resolve(value) {
//value是否为MyPromise这个类型的实例对象 .then后面调用.then
if (value instanceof MyPromise) {
return value.then(resolve, reject)
}
//去到下次的任务队列 保证then的执行顺序
setTimeout(() => { //状态发生变更就要回调
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value //获取到该参数 可能接收多个回调
that.resolvedCallbacks.map(cb => cb(that.value))
}
}, 0)
}
function reject(value) {
setTimeout(() => { //
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}, 0)
}
try { //调用成功
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
//原型挂上then方法 接收回调resolve reject
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
const promise2 = null
//传来的参数必须是个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {
throw r
}
//判断当前的状态
if (that.state === PENDING) {
return (promise2 = new MyPromise((resolve, reject) => {
that.resolvedCallbacks.push(() => {
try {
const x = onFulfilled(that.value)
resolutionProcedure(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
that.rejectedCallbacks.push(() => {
try {
//下个promise的回调 x为 第一层.then的执行结果
const x = onRejected(that.value)
//放置第二层执行.then
resolutionProcedure(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}))
// that.resolvedCallbacks.push(onFulfilled)
// that.rejectedCallbacks.push(onRejected)
}
//状态已经变更 就调用方法
if (that.state === RESOLVED) {
//返回一个新的promise对象
return (promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
//下个promise的回调 x为 第一层.then的执行结果
const x = onFulfilled(that.value)
//放置第二层执行.then
resolutionProcedure(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}))
// onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
// ...
}
}
function resolutionProcedure(promise2, x, resolve, reject) {
//前一个.then 和后一个 .then接收的对象不能一样
if (promise2 === x) { //不能抛出同一个promise对象
return reject(new TypeError('Error'))
}
if (x instanceof MyPromise) { //仍然是一个promise对象,继续抛出该对象,给下一个.then
x.then(function(value) {
resolutionProcedure(promise2, value, resolve, reject)
})
}
let called = false //控制执行回调仅为一次
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then////考虑可能会return new Promise.then({}),获取x的then方法
if (typeof then === 'function') {
then.call(
x,
y => { //只执行一次回调
if (called) return
called = true
resolutionProcedure(promise2, y, resolve, reject)
},
e => {
if (called) return
called = true
reject(e)
}
)
} else { //x不是函数
resolve(x) //说明不是promise对象,直接resolve
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
resolve(x)
}
}
总结
以上也只是个人理解,如有错误,请大家在评论当中指出,谢谢!我是小白,在js路上不折不扣!