js手写Promise原理
文章目录
前言
如果手写了promise那么,会对你有以下帮助
1,js功底更加深厚
2,有助于读懂大佬们的源码
3,熟练使用promise,你想你都把原理搞明白了那么使用个api还不得心应手
4,有助于面试,面试官常问
了解promise
1,Promise是一个类,可以翻译成承诺、许诺、期约。
2,Promise的状态一旦被确定下来,无法被更改,resolve、reject两个回调函数同时存在只能执行一个,那个在前执行那个。
3,Promise的三个状态
待定( pending ) : 初始状态,既没有被兑现,也没有被拒
已兑现( fulfilled ) : 意味着操作已经完成 resolve
已拒绝 (rejected):意味着操作失败 reject
promise初步基本结构
我们先初步搭建结构
1,我们新建一个HFPromise构造函数,HFPromise当中有一个函数被当做参数传递,且这个函数又有两个参数resolve与reject也是函数。
const promise = new HFPromise((resolve, reject) => {
debugger
// reject('111')
resolve('222')
})
那么我们的类应该这样写
1,promise穿过来一个参数executor为函数,这个函数立即执行,这个函数中有两个参数resolve与reject也是函数,所以我们要在类里创建方法,将resolve与reject在executor执行时传回去
2,我们知道promise是有三个状态的,所以我们要定义三个状态,用来判断。因为我们resolve成功和reject失败只能调一个不可能同时存在
3,我们知道resolve执行的时候是有参数的,例如resolve(‘222’),那么我们在类里创建的resolve方法要带接受222值,并保存下来
class HFPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
debugger
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
console.log('res');
this.value = value
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
console.log("rej");
this.reason = reason
}
}
executor(resolve, reject)
}
}
promise的then方法
1,在类里新建then方法,then方法中有两个箭头函数成功与失败,所以我们用参数接受一下这两个函数,并将其push到对应的数组中
2,调用多次then()应该都被执行,所以创两个数组一个成功一个失败,用来遍历执行
3,queueMicrotask是微任务,js是单线程的,但是怎么异步的呢。浏览器事件循环中,先执行顶层代码再执行完微任务队列后再执行宏任务队列。与栈相反,先加入队列的先执行。
宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
微任务队列(microtask queue):Promise的then回调、 Mutation Observer API、queueMicrotask()
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HFPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFNs = []
this.onRejectedFns = []
const resolve = (value) => {
debugger
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('res');
this.value = value
this.onFulfilledFNs.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
console.log("rej");
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
debugger
onFulfilled(this.value)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
onRejected(this.reason)
}
if (this.status === PROMISE_STATUS_PENDING) {
debugger
this.onFulfilledFNs.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
// this.onFulfilledFNs.push(onFulfilled)
// this.onRejectedFns.push(onRejected)
}
}
const promise = new HFPromise((resolve, reject) => {
debugger
// reject('111')
resolve('222')
})
// 调用两次then()应该都被执行,所以创两个数组一个成功一个失败,便利执行
promise.then(res => {
debugger
console.log(res);
}, err => {
console.log(err);
})
promise.then(res => {
debugger
console.log(res);
}, err => {
console.log(err);
})
//这里在延迟执行时,promise中onFulfilledFNs 和onRejectedFns数组已经添加完毕,去执行了
// 所以这边没办法执行,所以我们要根据状态判断直接执行这两个回调函数
setTimeout(() => {
promise.then(res => {
console.log(res);
}, err => {
console.log(err);
})
}, 1000)
promise的then方法链式调用
1,链式调用说明,then返回的是promise,所以我们改进一下then方法
2,then链式调用中上一个then的返回值是下一个then的成功值,并执行成功函数代码块 所以我们用try catch包裹
3,then链式调用中上一个then抛出异常是下一个then的失败值执行,并执行失败函数代码块 所以我们用try catch包裹
4,如果你在第一个promise中就抛出了异常那么我们也要将executor(resolve, reject)用try catch包裹
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HFPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFNs = []
this.onRejectedFns = []
const resolve = (value) => {
debugger
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('res');
this.value = value
this.onFulfilledFNs.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
console.log("rej");
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
debugger
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
debugger
this.onFulfilledFNs.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
})
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
})
}
})
}
}
const promise = new HFPromise((resolve, reject) => {
debugger
reject('111')
// resolve('222')
// throw new Error('message err----')
})
promise.then(res => {
debugger
console.log("res1", res);
// return "aaa"
}, err => {
console.log("err1", err);
return "bbb"
// throw new Error('message err')
}).then(res => {
console.log("res2", res);
}, err => {
console.log("err2", err);
})
promise的的catch方法
1, .catch是接在then后面的,then返回的是一个新promise,所以你在把错误传到then的时候,将会给到新的promise赋值
2,如何解决呢,我们在then里面做个判断,如果为空,则抛出异常,我们上面链式调用时已经测试了,err1抛出的异常,在err2打印
3,还有个小问题,你要判断onFulfilled是否有值,有值在push到数组中,这里我们用不到他,他是undifaned
promise1 undefined
promise2 err => {}
reject会去调第一个,但第一个是undefind 异常被加到第二个新promise里面了。第一个promise如何用第二个promise的异常?第一个抛出异常第二就接收异常就可以执行了
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HFPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFNs = []
this.onRejectedFns = []
const resolve = (value) => {
debugger
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('res');
this.value = value
this.onFulfilledFNs.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
console.log("rej");
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onRejected = onRejected || (err => { throw err })
return new Promise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
debugger
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
debugger
if (onFulfilled) this.onFulfilledFNs.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
})
if (onRejected) this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
})
}
})
}
catch(onRejected) {
this.then(onFulfilled, onRejected)
}
}
const promise = new HFPromise((resolve, reject) => {
debugger
// reject('111')
resolve('222')
// throw new Error('message err----')
})
// 链式调用
//1返回promise
//2 res1 err1返回的值 res2 err2打印
//3. res1 err1 抛出异常 res2 err2打印
//4. HFPromise抛出异常,err1打印
// promise.then(res => {
// debugger
// console.log("res1", res);
// // return "aaa"
// }, err => {
// console.log("err1", err);
// // return "bbb"
// throw new Error('message err')
// }).then(res => {
// console.log("res2", res);
// }, err => {
// console.log("err2", err);
// })
//1,.catch是接在then后面的,then返回的是一个新promise,所以你在把错误传到then的时候,将会给到新的promise赋值
//2,如何解决呢,我们在then里面做个判断,如果为空,则抛出异常,我们上面链式调用时已经测试了,err1抛出的异常,在err2打印
//3,还有个小问题,你要判断onFulfilled是否有值,有值在push到数组中,这里我们用不到他,他是undifaned
// promise1 undefined
// promise2 err => {}
// reject会去调第一个,但第一个是undefind 异常被加到第二个新promise里面了。第一个promise如何用第二个promise的异常?第一个抛出异常第二就接收异常就可以执行了
promise.then(res => {
console.log(res);
}).catch(err => {
console.log("caerr", err);
})
promise的resolve与reject方法
类里加成功和失败方法,这是promise自己的方法所以static
static resolve(value) {
return new HFPromise((resolve) => resolve(value))
}
static reject(reason) {
return new HFPromise((reject) => reject(reason))
}
调用
HFPromise.resolve('hello,world').then((res) => {
console.log(res);
})
HFPromise.reject('massage-err').then(err => {
console.log(err);
})
promise的finally方法
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HFPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFNs = []
this.onRejectedFns = []
const resolve = (value) => {
debugger
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('res');
this.value = value
this.onFulfilledFNs.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
console.log("rej");
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onRejected = onRejected || (err => { throw err })
return new Promise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
debugger
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
debugger
if (onFulfilled) this.onFulfilledFNs.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
})
if (onRejected) this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
})
}
})
}
catch(onRejected) {
this.then(onFulfilled, onRejected)
}
//不管成功或失败都要调finakky
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
}
const promise = new HFPromise((resolve, reject) => {
debugger
reject('111')
// resolve('222')
// throw new Error('message err----')
})
promise.then(res => {
console.log(res);
return 'aaaa'
}).then(res => {
console.log('res2', res);
return 'aaaa'
}).catch((err) => {
console.log('err', err);
}).finally(() => {
console.log('finally--');
})
promise的all方法
比较简单,认真阅读就看懂了,如果then链式调用搞懂其他都相对简单
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class HFPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFNs = []
this.onRejectedFns = []
const resolve = (value) => {
debugger
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
console.log('res');
this.value = value
this.onFulfilledFNs.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
console.log("rej");
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onRejected = onRejected || (err => { throw err })
return new Promise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
debugger
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
debugger
if (onFulfilled) this.onFulfilledFNs.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
})
if (onRejected) this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
})
}
})
}
catch(onRejected) {
this.then(onFulfilled, onRejected)
}
//不管成功或失败都要调finakky
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
static resolve(value) {
return new HFPromise((resolve) => resolve(value))
}
static reject(reason) {
return new HFPromise((reject) => reject(reason))
}
static all(arr) {
return new HFPromise((resolve, reject) => {
const promiseARR = []
arr.forEach(promise => {
promise.then(res => {
promiseARR.push(res)
if (promiseARR.length === arr.length) {
resolve(promiseARR)
}
}, err => {
reject(err)
})
})
})
}
}
const p1 = new promise(resolve => {
setTimeout(() => {
console.log('111');
}, 1000)
})
const p2 = new promise(resolve => {
setTimeout(() => {
console.log('222');
}, 2000)
})
const p3 = new promise(resolve => {
setTimeout(() => {
console.log('333');
}, 3000)
})
HFPromise.all([p1, p2, p3]).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})