Implementing promises should be accomplished on the basis of skillful use. First, there are a few things to be clear about
- Promise is a class
- Promise has three states:This is a big pity, which is my success and my failure, which must be postponed.
- pending -> fulfilled / rejected
- Once a state is established, it cannot be changed
- The resolve and reject functions are used to change state
- resolve: fulfilled
- reject: rejected
- Then is called with two parameters. The first parameter is the callback function when the state is fulfilled, and the second parameter is the callback function when the state is Rejected
First edition – Implementation of basic core principles
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING / / promise
value = undefined // The value on success then method needs to return
error = undefined // The value of the failed then method needs to be returned
// The function on success
reslove = value= > {
// Change the promise state
this.status = FULFILLED
this.value = value
}
// Function on failure
reject = error= > {
// Change the promise state
this.status = REJECTED
this.error = error
}
then(successCallback, failCallback) {
switch (this.status) {
case FULFILLED:
successCallback(this.value)
break
case REJECTED:
failCallback(this.error)
break}}}// test
const promise = new MyPromise((reslove, reject) = > {
reslove('reslove')
})
promise.then(value= > {
console.log(value) // reslove
}, error= > {
console.log(error)
})
Copy the code
With the above code, we successfully implemented the promise fundamentals. Calling the promise.then method successfully prints’ reslove ‘. However, we do not deal with PENDING state in the implementation of the THEN method. That is, if we do an asynchronous operation, the current promise cannot be processed. Such as:
const promise = new MyPromise((reslove, reject) = > {
setTimeout(() = > {
reslove('reslove')},2000)})Copy the code
Version 2 – Handles asynchronous cases
What about asynchrony? Using the setTimeout example above, reslove is executed after 2 seconds, and the state of the current Promise is still PENDING within 2 seconds of calling the THEN method. Two-step solution:
- PENDING state, success or failure callback is stored temporarily
- Reslove/Reject determines whether there is a callback and executes it if there is
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING / / promise
value = undefined // The value on success then method needs to return
error = undefined // The value of the failed then method needs to be returned
successCallback = undefined // Successful callback
failCallback = undefined // Failed callback
// The function on success
reslove = value= > {
// Change the promise state
this.status = FULFILLED
this.value = value
this.successCallback && this.successCallback(this.value)
}
// Function on failure
reject = error= > {
// Change the promise state
this.status = REJECTED
this.error = error
this.failCallback && this.failCallback(this.error)
}
then(successCallback, failCallback) {
switch (this.status) {
case FULFILLED:
successCallback(this.value)
break
case REJECTED:
failCallBack(this.error)
break
// Asynchronous case processing
case PENDING:
this.successCallback = successCallback
this.failCallback = failCallback
break}}}// test
const promise = new MyPromise((reslove, reject) = > {
setTimeout(() = > {
reslove('reslove')},2000)
})
promise.then(value= > {
console.log(value) // after 2 seconds: reslove
}, error= > {
console.log(error)
})
Copy the code
Once the second version was implemented, the problem arose again. When we normally use a promise, the then method under the same promise can be called multiple times. Let’s change the test code to
// test
const promise = new MyPromise((reslove, reject) = > {
setTimeout(() = > {
reslove('reslove')},2000)
})
promise.then(value= > {
console.log('First call then:', value)
})
promise.then(value= > {
console.log('Second call then:', value)
})
// Call then reslove the second time
Copy the code
Finding that the code only executed the last THEN method, print “second call THEN: reslove”. This is obviously not in line with our expectations
Version 3 – Multiple calls
To implement multiple calls, all we need to do is hold multiple callbacks as an array and call them in turn with reslove/ Reject
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING / / promise
value = undefined // The value on success then method needs to return
error = undefined // The value of the failed then method needs to be returned
// Array ephemeral
successCallback = [] // Successful callback
failCallback = [] // Failed callback
// The function on success
reslove = value= > {
// Change the promise state
this.status = FULFILLED
this.value = value
// call in sequence
while(this.successCallback.length) this.successCallback.shift()(this.value)
}
// Function on failure
reject = error= > {
// Change the promise state
this.status = REJECTED
this.error = error
// call in sequence
while(this.failCallback.length) this.failCallback.shift()(this.error)
}
then(successCallback, failCallback) {
switch (this.status) {
case FULFILLED:
successCallback(this.value)
break
case REJECTED:
failCallBack(this.error)
break
// Asynchronous case processing
case PENDING:
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
break}}}// test
const promise = new MyPromise((reslove, reject) = > {
setTimeout(() = > {
reslove('reslove')},2000)
})
promise.then(value= > {
console.log('First call then:', value)
})
promise.then(value= > {
console.log('Second call then:', value)
})
promise.then(value= > {
console.log('Third call then:', value)
})
// First call then: reslove
// Then: reslove
// Then: reslove
Copy the code
In addition to multiple calls, Promise also supports chained calls. The value returned by the then callback is the same as the value returned by the previous THEN callback.
Fourth edition – Chain calls
The important thing to note here is that we need to determine what type the return value of the previous THEN was. If it is a normal value, you can call the reslove method directly. In the case of a Promise object, you need to call reslove or Reject depending on what the Promise object returns.
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
exector(this.reslove, this.reject)
}
status = PENDING / / promise
value = undefined // The value on success then method needs to return
error = undefined // The value of the failed then method needs to be returned
successCallback = [] // Successful callback
failCallback = [] // Failed callback
// The function on success
reslove = value= > {
// Change the promise state
this.status = FULFILLED
this.value = value
while(this.successCallback.length) this.successCallback.shift()(this.value)
}
// Function on failure
reject = error= > {
// Change the promise state
this.status = REJECTED
this.error = error
while(this.failCallback.length) this.failCallback.shift()(this.error)
}
then(successCallback, failCallback) {
return new Promise((reslove, reject) = > {
switch (this.status) {
case FULFILLED:
reslovePromise(successCallback(this.value), reslove, reject)
break
case REJECTED:
failCallBack(this.error)
break
// Asynchronous case processing
case PENDING:
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
break}}}})// This is a big pity. // This is a big pity. // This is a big pity.
function reslovePromise(x, reslove, reject) {
if (x instanceof MyPromise) {
x.then(reslove, reject)
} else {
reslove(x)
}
}
// test
const promise = new MyPromise((reslove, reject) = > {
reslove('reslove')
})
promise.then(value= > {
console.log('First call then:', value)
return 'reslove2'
}).then(value= > {
console.log('Second call then:', value)
return new MyPromise((reslove, reject) = > {
reslove('reslove3')
})
}).then(value= > {
console.log('Third call then:', value)
})
// First call then: reslove
// Second call then: reslove2
// Then: reslove3
Copy the code
By version 4, our MyPromise implemented the basic functionality of promises. But as you can see from the code, we didn’t do any error handling for MyPromise, which is not what robust code looks like. So let’s do some code optimization and error handling for MyPromise.
Fifth edition – Code optimization and error handling
Error handling has two things in mind. Reject (reject); reject (reject); reject (reject); The second is to recognize that the promise returns from the object.
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(exector) {
try {
exector(this.reslove, this.reject)
} catch(e) {
this.reject(e)
}
}
status = PENDING / / promise
value = undefined // The value on success then method needs to return
error = undefined // The value of the failed then method needs to be returned
successCallback = [] // Successful callback
failCallback = [] // Failed callback
// The function on success
reslove = value= > {
// Change the promise state
this.status = FULFILLED
this.value = value
while(this.successCallback.length) this.successCallback.shift()()
}
// Function on failure
reject = error= > {
// Change the promise state
this.status = REJECTED
this.error = error
while(this.failCallback.length) this.failCallback.shift()()
}
then(successCallback, failCallback) {
// Make the arguments of the then method optional
successCallback = successCallback ? successCallback : value= > value
failCallback = failCallback ? failCallback : error= > {throw error}
let newPromise = new MyPromise((reslove, reject) = > {
switch (this.status) {
case FULFILLED:
setTimeout(() = > {
try {
reslovePromise(newPromise, successCallback(this.value), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)
break
case REJECTED:
setTimeout(() = > {
try {
reslovePromise(newPromise, failCallback(this.error), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)
break
// Asynchronous case processing
case PENDING:
this.successCallback.push(() = > {
setTimeout(() = > {
try {
reslovePromise(newPromise, successCallback(this.value), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)})this.failCallback.push(() = > {
setTimeout(() = > {
try {
reslovePromise(newPromise, failCallback(this.error), reslove, reject)
} catch(e) {
reject(e)
}
}, 0)})break}})return newPromise
}
}
// This is a big pity. This is a big pity. // This is a big pity.
function reslovePromise(newPromise ,x, reslove, reject) {
if (newPromise === x) {
return reject(new TypeError('Loop call'))}if (x instanceof MyPromise) {
x.then(reslove, reject)
} else {
reslove(x)
}
}
// test
const promise = new MyPromise((reslove, reject) = > {
setTimeout(() = > {
reslove('reslove')},2000)
// reject('reject')
})
promise.then(value= > {
console.log('First call then reslove:', value)
throw new Error('then error')},error= > {
console.log('First call then Reject:', error)
}).then(value= > {
console.log('Then reslove:', value)
}, error= > {
console.log('Second call then Reject:', error)
})
// Then reslove: reslove
// Then reject: Error: then Error
Copy the code