The introduction
Promise came along to solve the problem of callback hell in JS, to make the code cleaner, and was a specification and important feature in ES6. It’s easy to use, but do you know how it works? Now let’s take a look at how Promise actually works 😄
- Promise specification
The Promise specification is the rule that the Promise function needs to follow, and for THE Promise used in ES6, it followsPromise/A + specification.
Since there are rules to follow, let’s follow them step by stepPromise
.
1. Create the Promise class
See how promises are used
const promise = new Promise((resolve, reject) = > {
try {
resolve('123');
} catch(err) {
reject('error'); }}); promise .then((msg) = > {
console.log(msg)
})
Copy the code
- First of all it’s aA constructorAnd receive one
function
As a parameter,
And thisfunction
There areresolve
andreject
Two methods.resolve
Represents the value returned after success,reject
Represents reason for refusing to return
Create a class called MyPromise based on the use of the Promise
/** * @params {function} callback */
class MyPromise {
// The constructor accepts a function
constructor(callback) {
callback(this.resolve, this.reject); // Call this function
}
resolve = (value) = > {} // The resolve method executed in the callback
reject = (reason) = > {} // The reject method executed in the callback
}
/ / test
var test = new MyPromise((resolve, reject) = > {
console.log('my promise is running! ');
}) // Print my promise is running!
Copy the code
2. Three states
Now our class is ready to execute the methods we passed in, but what about the resolve and reject methods it passed in? Let’s move on to the Promise specification
- According to the specification
Promise
There are three statespending
(wait),fulfilled
(Finished),rejected
(Refused). - When the state of
pending
, Promise can be changed tofulfilled
orrejected
state - When the state of
fulfilled
, Promise cannot change its state; It must have a value and cannot be changed - When the state of
rejected
, Promise cannot change its state; There must be a reason for the rejection and there must be no change
Follow the Promise rule and write the class you just created:
const stateArr = ['pending'.'fulfilled'.'rejected']; // Three states
/** * @params {function} callback */
class MyPromise {
constructor(callback) {
this.state = stateArr[0]; // The current state
this.value = null; // The return value when finished
this.reason = null; // Failure cause
callback(this.resolve, this.reject); // Call this function
}
// The resolve method executed in the callback
resolve = (value) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; This is fulfilled. // This is fulfilled
this.value = value; // Write the final return value}}// The reject method executed in the callback
reject = (reason) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[2]; // Update the status to rejected
this.reason = reason; // Write the reason for the rejection}}}Copy the code
Test it out:
resolve
fulfilled
reject
3. Then method
Our MyPromise is written here, it can already implement status update and value pass, but how to output its value to our business? As you can see from the use of a Promise, it outputs values through the THEN method. Then is a necessary method. Take a look at the THEN specification:
- Promise must provide one
then
Method to access its current or final value or reason - In the promise
then
Method takes two parametersonFulilled
andonRejected
Here are the specifications (in part) for onFulilled and onRejected
onFulilled
andonRejected
Both are optional arguments:- if
onFulilled
It’s not a function, it has to be ignored - if
onRejected
It’s not a function, it has to be ignored
- if
- if
onFulilled
Is a function:- It must be called at fulfilled, in the promise method
value
As the first parameter - It must not be called before fulfilled
- It cannot be called more than once
- It must be called at fulfilled, in the promise method
- if
onRejected
Is a function:- It must be called when you rejected, in the promise method
reason
As the first parameter - It must not be called before Rejected
- It cannot be called more than once
- It must be called when you rejected, in the promise method
- Cannot be called until the execution context stack contains only platform code
onFulfilled
oronRejected
onFulfilled
andonRejected
It has to be a functionthen
It can be called multiple times within the same promisethen
You have to return apromise
Let’s design the THEN method according to the rules of the THEN function
const stateArr = ['pending'.'fulfilled'.'rejected']; // Three states
class MyPromise {
constructor(callback) {
this.state = stateArr[0]; // The current state
this.value = null; // The return value when finished
this.reason = null; // Failure cause
callback(this.resolve, this.reject); // Call this function
}
// The resolve method executed in the callback
resolve = (value) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; This is fulfilled. // This is fulfilled
this.value = value; // Write the final return value}}// The reject method executed in the callback
reject = (reason) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[2]; // Update the status to rejected
this.reason = reason; // Write the reason for the rejection}}/ / then method
then = (onFulilled, onRejected) = > {
// Judge if onFulilled and onRejected are functions, if not ignore it
onFulilled = typeof onFulilled === 'function' ? onFulilled : (value) = > value;
onRejected = typeof onRejected === 'function' ? onRejected : (reason) = > reason;
This is very depressing
if (this.state === stateArr[1]) {
// Then must return a promise
return new MyPromise((resolve, reject) = > {
try {
const result = onFulilled(this.value); // Execute the incoming onFulilled method
// If onFulilled returns a Promise, then the then method is called
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) { reject(err); }})}// If the state is rejected
if (this.state === stateArr[2]) {
// Then must return a promise
return new MyPromise((resolve, reject) = > {
try {
const result = onRejected(this.reason); // Execute the onRejected method passed in
// If onRejected returns a Promise, then call the then method
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) { reject(err); }}}Copy the code
Test it out:
Successful return:
Failure returns:
4. Keep improving
So far, MyPromise is almost ready to run, but there is a serious bug. If the resolve method does not execute in context on an asynchronous request, this will cause the then method to fail
var test = new MyPromise((resolve, reject) = > {
setTimeout((a)= > {
resolve(123);
}, 2000)
})
.then(msg= > {
console.log(msg);
return 456;
})
Copy the code
Because the state of the promise has not changed when the THEN method is called, and our THEN method does not have the logic to handle pending state. This causes the then method to not return anything when the asynchronous method is executed. For example, in the example above, JavScript has executed the THEN method, but the setTimeout method is still waiting in eventLoop. Here’s what you need to do:
- will
then
Save the method in and waitresolve
orreject
Then call the one you just savedthen
The methods in - Because there may be multiple
then
Methods are executed, so you need a data store for them
Based on these two points, let’s change some code
// Add two new arrays in constructor to install the resolve and reject methods in THEN
constructor(callback) {
this.resolveArr = [];
this.rejectArr = [];
}
// Modify the resolve method
resolve = (value) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; This is fulfilled. // This is fulfilled
this.value = value; // Write the final return value
this.resolveArr.forEach(fun= > fun(value)) // Loop through the resolve method inserted by then}}// Modify the reject method
reject = (reason) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; This is fulfilled. // This is fulfilled
this.reason = reason; // Write the final return value
this.rejectArr.forEach(fun= > fun(reason)) // The loop executes the then inserted reject method}}// We need to add logic to the THEN method to capture pending state
then = (onFulilled, onRejected) = > {
// If the state is pending
if (this.state === stateArr[0]) {
return new Promise((resolve, reject) = > {
// The function called when the insert was successful
this.resolveArr.push((value) = > {
try {
const result = onFulilled(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) { reject(err); }})// The function called when the insert failed
this.rejectArr.push((value) = > {
try {
const result = onRejected(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) {
reject(err)
}
})
})
}
}
Copy the code
There you go. Test it
new MyPromise((resolve, reject) = > {
setTimeout((a)= > {
resolve(123);
}, 2000)
})
.then(msg= > {
console.log(msg);
return new MyPromise((resolve, reject) = > {
setTimeout((a)= > {
resolve(456)},2000);
})
})
.then(msg= > {
console.log(msg);
})
Copy the code
5.Promise’s other approach
The implementation of promises according to the Promise specification is almost complete. Finally, we will add the methods implemented in the Promise
- Catch method
// Implement in the MyPromise prototype
class MyPromise {
// Call reject in THEN
catch = (reject) = > {
this.then(null, reject); }}Copy the code
- resolve
MyPromise.resolve = (value) = > {
return new MyPromise((resolve, reject) = > { resolve(value) });
}
Copy the code
- reject
MyPromise.resolve = (reason) = > {
return new MyPromise((resolve, reject) = > { reject(reason) });
}
Copy the code
- There are
all
.race
.Finally (prototype method)
In fact, it is based onPrototype in PromiseMethods andPromise the rulesImplementation, here is not a list of it. Those who need to know canTo go to the
The final code (The source address)
const stateArr = ['pending'.'fulfilled'.'rejected']; // Three states
class MyPromise {
constructor(callback) {
this.state = stateArr[0]; // The current state
this.value = null; // The return value when finished
this.reason = null; // Failure cause
this.resolveArr = [];
this.rejectArr = [];
callback(this.resolve, this.reject); // Call this function
}
// The resolve method executed in the callback
resolve = (value) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; This is fulfilled. // This is fulfilled
this.value = value; // Write the final return value
this.resolveArr.forEach(fun= > fun(value)) // Loop through the resolve method inserted by then}}// The reject method executed in the callback
reject = (reason) = > {
// Determine whether the state needs to be pending
if (this.state === stateArr[0]) {
this.state = stateArr[1]; This is fulfilled. // This is fulfilled
this.reason = reason; // Write the final return value
this.rejectArr.forEach(fun= > fun(reason)) // The loop executes the then inserted reject method}}/ / then method
then = (onFulilled, onRejected) = > {
// Judge if onFulilled and onRejected are functions, if not ignore it
onFulilled = typeof onFulilled === 'function' ? onFulilled : (value) = > value;
onRejected = typeof onRejected === 'function' ? onRejected : (reason) = > reason;
// If the state is pending
if (this.state === stateArr[0]) {
return new MyPromise((resolve, reject) = > {
// The function called when the insert was successful
this.resolveArr.push((value) = > {
try {
const result = onFulilled(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) { reject(err); }})// The function called when the insert failed
this.rejectArr.push((value) = > {
try {
const result = onRejected(value);
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) {
reject(err)
}
})
})
}
This is very depressing
if (this.state === stateArr[1]) {
// Then must return a promise
return new MyPromise((resolve, reject) = > {
try {
const result = onFulilled(this.value); // Execute the incoming onFulilled method
// If onFulilled returns a Promise, then the then method is called
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) { reject(err); }})}// If the state is rejected
if (this.state === stateArr[2]) {
// Then must return a promise
return new MyPromise((resolve, reject) = > {
try {
const result = onRejected(this.reason); // Execute the onRejected method passed in
// If onRejected returns a Promise, then call the then method
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else{ resolve(result); }}catch(err) { reject(err); }}}})// Call reject in THEN
catch = (reject) = > {
this.then(null, reject);
}
}
MyPromise.resolve = (value) = > {
return new MyPromise((resolve, reject) = > { resolve(value) });
}
MyPromise.resolve = (reason) = > {
return new MyPromise((resolve, reject) = > { reject(reason) });
}
Copy the code
summary
This time we learned how the promise was realized:
- Must be a constructor
- Three states (pending, resolve, reject)
- Then methods (the methods that promise must have)
Start with the constructor, go through the implementation of the three states, and finally implement the THEN method to implement the Promise according to the Promise rules. When you’re done, hand write a Promise in front of the interviewer. 😄