本文的目的主要是为了带大家认识Promise实现原理,只实现主要流程,会分为三个大知识点给大家分享Promise
目录
- Promise实现原理1【如何实现一个最基础的Promise】
- Promise实现原理2【如何实现链式调用】
- Promise实现原理3【如何实现Promise执行reject后如何停掉Promise的链式调用】
Promise基本用法
new Promise((resolve, reject) => {
resolve('成功')
reject('失败')
}).then(res => {
console.log(res)
}, err => {
console.log(err)
})
初步分析可得
- 首先 Promise 是一个构造函数
- 这个构造函数接收一个回调函数作为参数初始化时即刻执行(官方这样说到“The executor function is called with the ability to resolve or reject the promise”),立即执行后返回两个函数 resolve、reject成功和失败。
- 当执行reslove时会执行then的第一个参数,当执行reject时会执行then的第二个参数
初步编写可得
class Promise {
constructor(executor) {
const resolve = (res) => {
this.resFn(res)
}
const reject = (error) => {
this.errorFn(error)
}
// 初始化时立即执行
executor(resolve, reject)
}
then(resFn, errorFn) {
// 把函数放置到resolve 和 reject可访问的地方
this.resFn = resFn
this.errorFn = errorFn
}
}
这个时候我们会发现调用reject、resolve会报错 this.resFn is not a function 或 this.errorFn is not a function是因为我们在执行 resolve 时还没有执行 then方法,回调函数还没有挂载,我们需要等待主线程执行完成后再执行then不能同步执行。重点:通过 queueMicrotask 使用微任务。
微任务
class Promise {
constructor(executor) {
const resolve = (res) => {
queueMicrotask(() => {
this.resFn(res)
})
}
const reject = (error) => {
queueMicrotask(() => {
this.errorFn(res)
})
}
// 初始化时立即执行
executor(resolve, reject)
}
then(resFn, errorFn) {
// 把函数放置到resolve 和 reject可访问的地方
this.resFn = resFn
this.errorFn = errorFn
}
}
这时候我们的then就实现了一个异步调用。
Promise 状态
官网这样描述Promise状态
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
- Promise初始状态为pending,一旦状态发生改变不可再进行修改。
由此可得
class Promise {
constructor(executor) {
// 把三种状态设置为常量
const STATE_PENDING = 'pending',
STATE_FULFILLED = 'fulfilled',
STATE_REJECTED = 'rejected'
// 初始状态
this.state = STATE_PENDING
const resolve = (res) => {
// 只有状态为等待中时才允许修改状态
if (this.state === STATE_PENDING) {
queueMicrotask(() => {
this.state = STATE_FULFILLED
this.resFn(res)
})
}
}
const reject = (error) => {
// 只有状态为等待中时才允许修改状态
if (this.state === STATE_PENDING) {
queueMicrotask(() => {
this.state = STATE_REJECTED
this.errorFn(res)
})
}
}
// 初始化时立即执行
executor(resolve, reject)
}
then(resFn, errorFn) {
// 把函数放置到resolve 和 reject可访问的地方
this.resFn = resFn
this.errorFn = errorFn
}
}
这样一个简易的Promise函数就实现了,多次调用then会被重置。那如何实现多次调用呢?接下来我们来优化then方法实现多次调用。
优化then方法实现多次调用
基础实现
class Promise {
constructor(executor) {
// 把三种状态设置为常量
const STATE_PENDING = 'pending',
STATE_FULFILLED = 'fulfilled',
STATE_REJECTED = 'rejected'
// then队列
this.errorFnList = []
this.resFnList = []
// 初始状态
this.state = STATE_PENDING
const resolve = (res) => {
// 只有状态为等待中时才允许修改状态
if (this.state === STATE_PENDING) {
queueMicrotask(() => {
this.state = STATE_FULFILLED
// this.resFn(res)
this.resFnList.forEach(fn => fn(res))
})
}
}
const reject = (error) => {
// 只有状态为等待中时才允许修改状态
if (this.state === STATE_PENDING) {
queueMicrotask(() => {
this.state = STATE_REJECTED
// this.errorFn(res)
this.errorFnList.forEach(fn => fn(res))
})
}
}
// 初始化时立即执行
executor(resolve, reject)
}
then(resFn, errorFn) {
// 把函数放置到resolve 和 reject可访问的地方
// this.resFn = resFn
// this.errorFn = errorFn
this.resFnList.push(resFn)
this.errorFnList.push(errorFn)
}
}
const p1 = new Promise((resolve, reject) => {
resolve(2323)
})
// 这样我们多次调用的时候就不会冲掉上次的结果啦
p1.then(res => console.log(res))
p1.then(res => console.log(res))
// 但如果我们在异步队列里面调用
setTimeout(() => {
// 这个时候then不调用了
p1.then(res => console.log(res))
})
是不是这样就万事大吉利了呢?按照电视剧剧情发展当然不是,必定是有坑点的,那么接下来我们就看看坑点在哪
解决坑点
这个时候我们添加一个宏任务,再执行then调用时发现 then失效了不会被调用,其实是因为我们的状态 queueMicrotask 时发生了变化 “一旦状态发生改变不可再进行修改。”
class Promise {
// 把三种状态设置为常量
static STATE_PENDING = 'pending';
static STATE_FULFILLED = 'fulfilled';
static STATE_REJECTED = 'rejected';
constructor(executor) {
// then队列
this.errorFnList = []
this.resFnList = []
// 返回值
this.value = undefined
this.error = undefined
// 初始状态
this.state = Promise.STATE_PENDING
const resolve = (res) => {
// 只有状态为等待中时才允许修改状态
if (this.state === Promise.STATE_PENDING) {
queueMicrotask(() => {
this.state = Promise.STATE_FULFILLED
// this.resFn(res)
this.value = res
this.resFnList.forEach(fn => fn(res))
})
}
}
const reject = (error) => {
// 只有状态为等待中时才允许修改状态
if (this.state === Promise.STATE_PENDING) {
this.error = error
queueMicrotask(() => {
this.state = Promise.STATE_REJECTED
// this.errorFn(error)
this.error = error
this.errorFnList.forEach(fn => fn(error))
})
}
}
// 初始化时立即执行
executor(resolve, reject)
}
then(resFn, errorFn) {
// 把函数放置到resolve 和 reject可访问的地方
// this.resFn = resFn
// this.errorFn = errorFn
// 如果在调用then时状态已经确定,则直接调用
if (this.state === Promise.STATE_FULFILLED) {
resFn && resFn(this.value)
} else if (this.state === Promise.STATE_REJECTED) {
errorFn && errorFn(this.error)
} else {
this.resFnList.push(resFn)
this.errorFnList.push(errorFn)
}
}
}
p1.then(res => console.log(res))
setTimeout(() => {
// 这样就能正常执行了
p1.then(res => console.log(res))
})
核心知识点
-
回调机制
-
使用 queueMicrotask 微任务
-
then实现多次调用
下一篇:Promise实现原理2【如何实现链式调用】