Table of contents
One: First analyze the official promise
Two: Handwritten Promise-then method design
Three: then method optimization:
Four: Promise-catch method design
Five: Promise-finally method design
//This article takes you to realize a basic promise. Too many boundary conditions are not a test, just understand the main implementation process and logic
//For the problems that arise one by one, I will gradually analyze the reasons and corresponding solutions
//The premise is that you must master the basic use of promises and have a corresponding understanding of callback functions
//Step 1: Preliminary implementation of the simplest Promise (write it down step by step, Xiaobai can understand it, of course, you can skip some nonsense directly~~~)
One: First analyze the official promise
//---首先分析官方的promise
const p1 = new Promise((resolve, reject) => {
resolve(2222)
reject(33333)
})
// 调用then方法 then 方法接收2个回调函数作为参数
p1.then(res => { //只会打印222, 因为promise的状态一但由pending的状态改变了 就无法再次改变
console.log("res1:", res)
}, err => {
console.log("err:", err)
})
//Analysis:
//It can be seen that the parameter of promise receives a function, and receives 2 callback functions as parameters in the function, and then calls the resolve method
//Is executing the then method. The then function receives two parameters, both of which are callback functions. After executing resolve(2222), it will come to the callback function of the first parameter of then
// After executing reject(33333), it will come to the second parameter callback function of then-
//1. Then the preliminary writing can be obtained:
const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING //1.初始化状态 pending
this.value = undefined //2.保存参数
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) { //3. 只有pending状态才可改变
this.status = PROMISE_STATUS_FULFILLED
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
}
})
executor(resolve, reject) //在new myPromise 时初始化立即调用传递过来的函数
}
}
Two: Handwritten Promise-then method design
//2. Executing resolve or reject will call the then method (changed on the basis of the above code)
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.resfn(value)
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
this.errfn(error)
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
this.resfn = resfn
this.errfn = errfn
}
}
//(See if the code is the most basic implementation) Next, we execute the promise we implemented to see if it is normal
const p1 = new myPromise((resolve, reject) => {
resolve(111)
})
p1.then(res => {
console.log(res);
}, err => {
})
//After execution, you will find an error message: this.resfn is not a function
//So why is it reporting an error? Because it is related to the execution order of the code, when we execute resolve in new myPromise(), we will come to the resolve method
//And your then method duck soup is not executed, so it reports an error
//Solution: Just let then be executed first, and add resolve or reject to the asynchronous queue when the then method is to be executed
Above code:
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
//大家可能会想到使用setTimeout 然后0秒执行,但此处使用queueMicrotask 会更加合适
// setTimeout(() => {
// this.resfn(value)
// }, 0);
queueMicrotask(() => { //queueMicrotask: 主线程执行完毕之后立马执行
this.resfn(value)
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.errfn(error)
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //4.then 方法接收2个回调函数作为 参数 : 第一个为成功的回调,第二个失败的回调
this.resfn = resfn
this.errfn = errfn
}
}
//举一个栗子:
setTimeout(() => {
console.log('setTimeout');
}, 0);
queueMicrotask(() => {
console.log('queueMicrotask');
});
//实际运行
queueMicrotask
setTimeout
Then execute:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
reject(333333)
})
p1.then(res => { // final print 1111
console.log(res);
}, err => {
console.log(err);
})
So far our simplest implementation has been completed, and then we continue to optimize the then method :
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
queueMicrotask(() => {
this.value = value
this.resfns.forEach(fn => { //循环依次调用
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) { //将多次调用的then 方法保存到数组中,依次调用
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = [] //1.多次调用then 时用数组 保存
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.如果在then调用的时候, 状态已经确定下来,那么就可以直接调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
resfn(this.value)
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
errfn(this.error)
}
//2. 相反 对与已经确定的状态 就不需要在push 执行了
if (this.status === PROMISE_STATUS_PENDING) {
this.resfns.push(resfn)
this.errfns.push(errfn)
}
}
}
implement:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
// reject(333333)
})
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
// Call then method to call multiple times
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
// After determining the state of the Promise, call then again
setTimeout(() => {
p1.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
result:
res1: 111
res2: 111
res3:111
Three: then method optimization:
For our previous then method, multiple calls are called through the instantiated class
For example:
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
This kind of call is achievable with our current code, but what if it is called in a chain all the time? Let's talk nonsense and go to the code:
const p1 = new myPromise((resolve, reject) => {
resolve(111)
reject(333333)
})
p1.then(res => {
console.log("The first successful callback:", res)
return "Second success callback"
}, err => {
console.log("err1:", err)
return "Second failure callback"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
Now our code will definitely not work (you can test it in your own environment) Next, continue to optimize our code:
Before optimizing, first analyze a wave: (The official promise chain call is used as an example to explain)
1. Execute resolve
2. Call the then method, the value returned by the then method is actually new myPromse((resolve,reject) => resolve("The second successful callback")), and the return value is passed on with the resolve() method
So after printing "the first successful callback:", 111, print "res2:" again, the second successful callback,
3. So when will the "err2:" second failure callback be printed? It only needs to throw an exception when calling then for the first time, and err2 will be executed.
For example:
p1.then(res => {
console.log("The first successful callback:", res)
throw new Error("err message")
}, err => {
console.log("err1:", err)
return "Second failure callback"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
Results of the:
"First successful callback:", 111
"err2: Error: err message
2. If reject is executed, in the second callback of reject
err => {
console.log("err1:", err)
return "Second failure callback"
}
If the value is returned, will console.log("res2:", res) or console.log("err2:", err) be executed next?
For example:
const p1 = new myPromise((resolve, reject) => {
reject(333333)
})
p1.then(res => {
console.log("The first successful callback:", res)
return "Second success callback"
}, err => {
console.log("err1:", err)
return "Second failure callback"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
Here I will tell you that console.log("res2:", res) will actually be executed. If you want to execute the code block locked by console.log("err2:", err), you need to return "the second failure callback "Change to throw an exception to execute
chestnut:
p1.then(res => {
console.log("The first successful callback:", res)
return "Second success callback"
}, err => {
console.log("err1:", err)
throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
The logic has been clarified and then expressed in code: (you can copy the following code, and then test it yourself with the above case to make it clearer)
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
})
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
})
}
}
//然后测试:
const p1 = new myPromise((resolve, reject) => {
// resolve(111)
reject(333333)
})
p1.then(res => {
console.log("res1:", res)
return "第二次的成功回调"
}, err => {
console.log("err1:", err)
throw new Error("err message")
// return "第二次的失败回调"
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
Four: Promise-catch method design
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.难点:利用抛错让下一个promise的catch帮忙处理 防止catch方法让链式调用断层
const defaultOnRejected = err => {
throw err
}
errfn = errfn || defaultOnRejected
return new myPromise((resolve, reject) => {
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (resfn) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
}
})
}
if (errfn) {
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
}
})
}
catch (errfn) { //2.catch 方法
return this.then(undefined, errfn)
}
}
//接着测试代码:
const p1 = new myPromise((resolve, reject) => {
reject(333333)
})
p1.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//结果:
err: 333333
Five: Promise-finally method design
const PROMISE_STATUS_PENDING = 'pending' //定义三种状态常量
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.error = undefined
this.resfns = []
this.errfns = []
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return //避免 调用resolve 后又调用 reject 多次执行
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.resfns.forEach(fn => {
fn(this.value)
})
})
}
})
const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.error = error
this.errfns.forEach(fn => {
fn(this.error)
})
})
}
})
executor(resolve, reject)
}
then(resfn, errfn) {
// 1.利用抛错让下一个promise的catch帮忙处理 防止catch方法让链式调用断层
const defaultOnRejected = err => {
throw err
}
errfn = errfn || defaultOnRejected
const defaultOnFulfilled = value => {
return value
}
resfn = resfn || defaultOnFulfilled
return new myPromise((resolve, reject) => { //1. 直接new 一个mypromise 作为then 方法的返回值 既可实现 .then.then.thne.then.....等等链式调用
if (this.status === PROMISE_STATUS_FULFILLED && resfn) {
try { //2. 异常处理:若成功则继续执行then链式调用 的第一个回调,失败则执行then 的第二个回调
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && errfn) {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
if (resfn) {
this.resfns.push(() => { //push 回调函数
try {
const value = resfn(this.value)
resolve(value)
} catch (err) {
reject(err) //tips:****利用抛出的错误 使得下一个promise的catch帮忙处理
}
})
}
if (errfn) {
this.errfns.push(() => {
try {
const value = errfn(this.error)
resolve(value)
} catch (err) {
reject(err)
}
})
}
}
})
}
catch (errfn) { //2.catch 方法
return this.then(undefined, errfn)
}
finally(fn) {
setTimeout(() => {
fn()
}, 0)
}
}
The rest are relatively simple class methods. I won’t talk about it here~~bye~