preface
Many JavaScript beginners have felt the fear of callback hell until they mastered the Promise syntax. While many languages already have Promise built in, it’s the $.ajax refactoring of jQuery 1.5 that really takes iT to the next level in JavaScript, supporting Promise and using it in the same way that jQuery advocates chain calls. Then ES6 came out, we started to enter the era of universal Promise, and then ES8 introduced async syntax, making JavaScript asynchronous writing more elegant. Today we’ll take a step-by-step approach to implementing a Promise. If you haven’t used promises yet, familiarize yourself with the Promise syntax before reading this article.
The constructor
In the existing Promise/A+ specification, it is not specified where the Promise object comes from. In jQuery, the Promise object is obtained by calling $.Deferred(), and in ES6, the Promise object is obtained by instantiating the Promise class. Here we use the ES syntax to construct a class that returns a Promise object by instantiating it.
::: tip PROMISE three states:
- Pending: Pending, which is the initial state of a Promise;
- Depressing: This is fulfilled, and the state of resolve will be called normally.
- Rejected: Rejected, an internal error, or the state after a call to reject.
: : :
We can see that promises have a state at run time, stored in [[PromiseState]]. Let’s add a state for MyPromise.
// Base variable definition
const STATUS = {
PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED'
}
class MyPromise {
constructor(callback) {
this.status = STATUS.PENDING
const resolve = () = > {
// TODO
}
const reject = () = > {
// TODO
}
try {
callback(resolve, reject)
} catch (error) {
Reject if there is an exception
reject(error)
}
}
}
Copy the code
The internal result
In addition to the state, a Promise also has a result [[PromiseResult]], which is used to temporarily store the values accepted by resolve/ Reject.
Go ahead and add an internal result to the constructor.
const STATUS = {
PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED'
}
class MyPromise {
constructor(callback) {
this.value = undefined
this.status = STATUS.PENDING
const resolve = (resolveR) = > {
this.value = resolveR
// TODO
}
const reject = (rejectR) = > {
this.value = rejectR
// TODO
}
try {
callback(resolve, reject)
} catch (error) {
Reject if there is an exception
reject(error)
}
}
}
Copy the code
Change state
Next, we need to implement the resolve and Reject methods, which change the state of the Promise object when called. Only promises that are in the pending state can be changed.
const STATUS = {
PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED'
}
class MyPromise {
constructor(callback) {
this.value = undefined
this.status = STATUS.PENDING
const resolve = (resolveR) = > {
if(this.status === STATUS.PENDING){
this.status = STATUS.FULFILLED
this.value = resolveR
}
}
const reject = (rejectR) = > {
if(this.status === STATUS.PENDING){
this.status = STATUS.REJECTED
this.value = rejectR
}
}
try {
callback(resolve, reject)
} catch (error) {
Reject if there is an exception
reject(error)
}
}
}
Copy the code
Then and catch methods
Next we need to implement the then and catch methods, and those of you who have used promises know that then and catch must return a Promise object.
const STATUS = {
PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED'
}
class MyPromise {
constructor(callback) {
this.value = undefined
this.status = STATUS.PENDING
const resolve = (resolveR) = > {
if(this.status === STATUS.PENDING){
this.status = STATUS.FULFILLED
this.value = resolveR
}
}
const reject = (rejectR) = > {
if(this.status === STATUS.PENDING){
this.status = STATUS.REJECTED
this.value = rejectR
}
}
try {
callback(resolve, reject)
} catch (error) {
Reject if there is an exception
reject(error)
}
}
then (resolveCb) {
switch(this.state){
case STATUS.FULFILLED:
resolveCb(this.value)
default:}return this
}
catch (rejectedCb) {
switch(this.state){
case STATUS.REJECTED:
rejectedCb(this.value)
default:}return this}}Copy the code
The finally method
The finally method is easy to implement.
const STATUS = {
PENDING: 'PENDING'.FULFILLED: 'FULFILLED'.REJECTED: 'REJECTED'
}
class MyPromise {
constructor(callback) {
this.value = undefined
this.status = STATUS.PENDING
const resolve = (resolveR) = > {
if(this.status === STATUS.PENDING){
this.status = STATUS.FULFILLED
this.value = resolveR
}
}
const reject = (rejectR) = > {
if(this.status === STATUS.PENDING){
this.status = STATUS.REJECTED
this.value = rejectR
}
}
try {
callback(resolve, reject)
} catch (error) {
Reject if there is an exception
reject(error)
}
}
then (resolveCb) {
switch(this.state){
case STATUS.FULFILLED:
resolveCb(this.value)
default:}return this
}
catch (rejectedCb) {
switch(this.state){
case STATUS.REJECTED:
rejectedCb(this.value)
default:}return this
}
finally (finallyCb) {
finallyCb()
}
}
Copy the code
Afterword.
Here is just a simple implementation of a promise, for a complete promise, of course, there are a lot of unrealized, such as:
:: tip
- The Promise class also provides two static methods that directly return the resolve/ Reject Promise object whose state has been fixed.
- In the Promise/A+ specification, it is explicitly stated that the then method returns A new Promise object rather than returning this directly;
- If no arguments are passed in when we call then, the current promise value can be passed transparently to the next THEN method, according to the specification
- Implementation of promise.all() and promise.race() methods
: :
I look forward to your exploration.