Introduction of the overall process
- Introduction of the overall process
- 1. Define the overall structure
- Implement the Promise constructor
- 3. Implement the THEN method
- 3. Implement the catch method
- 4. Realize the Promise. Resolve
- 5. Realize the Promise. Reject
- 6. Realize the Promise. All
- 7. Realize the Promise. Race
The article will explain why this is done with examples, in as vulgar and understandable a way as I can. If you have any questions or suggestions, please comment below. Project source code has been in the making, convenient point a star bai: https://github.com/Sunny-lucking/howToBuildMyPromise
1. Define the overall structure
- Write the constructor first, exposing the Promise outward
/ *Customize the Promise function module: IIFE* /
(function (window) {
/ *Promise constructorExecutor: An executor function* / function Promise(executor) { } // Expose the Promise to the outside window.Promise = Promise }) () Copy the code
- Add the method on the Promise prototype object
/ *The THEN of the Promise prototype objectSpecify a success/failure callback functionReturn a new PROMISE object* /
Promise.prototype.then = function(onResolved,onRejected){ } / *Promise the catch of the prototype objectSpecifies a failed callback functionReturn a new PROMISE object* / Promise.prototype.catch = function(onRejected){ } Copy the code
- Add the method on the Promise function object
/ *The Resovle method of the Promise function objectReturn a PROMISE object with the specified result* /
Promise.resolve = function(value){
} / *The REJECT method of the Promise function objectReturn a promise object with the failure status specified by reason* / Promise.reject = function(value){ } / *The all method of the Promise function objectReturn a Promise object. The returned promise state succeeds only if all promises are successful* / Promise.all = function(0value){ } / *The RACE method of the Promise function objectReturn a Promise object whose state is determined by the first completed promise* / Promise.race = function(value){ } Copy the code
You can see from the comments above. The result of execution of a method on either a Promise prototype object or a Promise function object returns a Promise object
Implement the Promise constructor
Let’s see how we use Promise
const promiseA = new Promise( (resolve,reject) = > {
resolve(777);
});
Copy the code
- We pass in a function, and it executes immediately, not only that, but also resolve and reject. The constructor has resolve and reject methods. Therefore, we can initially achieve:
/ *Promise constructorExecutor: An executor function* /
function Promise(executor) {
function resovle() { } function reject() { } // Execute Executor synchronously immediately executor(resovle,reject) } Copy the code
- Each promise has a state that can be pending or resolved, rejected. And the initial state is pending. Therefore, you need to add a status to represent the current promise state. And each promise has its own data.
function Promise(executor) {
var self = self
The new code self.status = 'pending' // Give the promise object a status attribute with an initial value of pending
self.data = undefined // Give the Promise object a data to store the result function resovle() { } function reject() { } // Execute Executor synchronously immediately executor(resovle,reject) } Copy the code
Moreover, when we use promises in this way,
// 例1
var promise = new Promise((resovle,reject) = >{
})
promise.then(resolve= >{},reject=>{}) Copy the code
In this case, we need an array of Callbacks to save the callbacks in the then, so the promise is still pending because the immediate function we passed in did not execute resolve or reject
function Promise(executor) {
var self = self
self.status = 'pending' // Give the promise object a status attribute with an initial value of pending
self.data = undefined // Give the Promise object a data to store the result The new code self.callbacks = [] {onResolved(){}, onRejected(){}} function resovle() { } function reject() { } // Execute Executor synchronously immediately executor(resovle,reject) } Copy the code
How does the then function collect incoming callbacks? If the promise is pending, push the callback to the callbacks.
Promise.prototype.then = function(onResolved,onRejected){
var self = this
if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved') { }else{ } } Copy the code
- Based on example 1 above, when we perform resovle (value), as in Example 2
// 例2
var promise = new Promise((resolve,reject) = >{
setTimeout(function () {
resolve(1)
})
}) promise.then( value= >{console.log(value)}, err=>{console.log(err)} ) Copy the code
How does the code execute at this point?
- The new Promise thread executes the new Promise code, and then finds a timer. The JS thread assigns the timer to the timer thread. 2. Then continue to execute the following code and find that it is THEN and that the current promise is still pending. Just put the callback function from the then into the Callbacks.
- After 5 seconds, the timer thread puts the callback function (i.e., macro task) in the message queue. The JS thread finds the macro task in the message queue and executes it.
- Resolve (1) is executed, and the callback in the Promise’s callbacks is executed. Change the promise state to Resolved. The 1 is then saved to the current promise object
So how do you make resolve? Resovle executes functions in Callbacks, saves data, and changes the promise state to resolved. So we can do it this way
function resolve(value) {
// Change the state to Resolved
self.status = 'resolved'
// Save the value of value
self.data = value
// If the callback function is waiting to be executed, immediately execute the onResolved callback asynchronously if (self.callbacks.length>0) { self.callbacks.forEach(callbackObj= >{ callbackObj.onResolved(value) }) } } Copy the code
- We also know that the state of a PROMISE can only change once, so when we execute resolve, we must determine whether the promise state is pending or not. Otherwise, we cannot execute
function resolve(value) {
// If the current state is not pending, no execution is performed
if(this.status ! = ='pending') { return
}
// Change the state to Resolved this.status = 'resolved' // Save the value of value this.data = value // If the callback function is waiting to be executed, immediately execute the onResolved callback asynchronously if (this.callbacks.length>0) { setTimeout((a)= >{ this.callbacks.forEach(callbackObj= >{ A callbackObj.onResolved(value) }) }) } } Copy the code
- The nice thing about this is that it’s the same with reject, so there’s no need to go into it here
function reject(value) {
// If the current state is not pending, no execution is performed
if(self.status ! = ='pending') { return
}
// Change the state to rejected self.status = 'rejected' // Save the value of value self.data = value // If the callback function is waiting to be executed, immediately execute the onResolved callback asynchronously if (self.callbacks.length>0) { self.callbacks.forEach(callbackObj= >{ callbackObj.onRejected(value) }) } } Copy the code
- We also know that when executing an Executor, the promise state executes the Reject method directly if an exception is made. For example,
/ / 3
var promise = new Promise((resolve,reject) = >{
The error; There was an error at this point
setTimeout(function () { resolve(1) }) }) Copy the code
To do this, we can use a try catch outside of the Executor
try{
// Execute Executor synchronously immediately
executor(resolve,reject)
}catch (e) { // If the actuator throws an exception, the Promise object becomes the rejected state
reject(e)
} Copy the code
All right, let’s test it out
// 例4
let promise = new Promise((resolve,reject) = >{
setTimeout(function () {
resolve(1)
//reject(1) },100) }) promise.then( value= >{ console.log("onResolved:",value); }, reason=>{ console.log("onRejected:",reason); } ) Copy the code
Find success. OnResolved :1 is displayed successfully
3. Implement the THEN method
We simply implement the situation where the promise is pending, such as:
Promise.prototype.then = function(onResolved,onRejected){
var self = this
if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved') { }else{ } } Copy the code
What about the other cases?
When the promise is in the ‘THEN’ state, the callback function in the ‘then’ state can be saved. When the promise is in the ‘THEN’ state, the callback function can be saved. When the promise is in the ‘THEN’ state, the callback function can be saved. Note that the execution is asynchronous. And as a microtask, here we simply use setTimeout to implement it.
Promise.prototype.then = function(onResolved,onRejected){
var self = this
if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved') { setTimeout((a)= >{ onResolved(self.data) }) }else{ setTimeout((a)= >{ onResolved(self.data) }) } } Copy the code
We also know that a new promise will be returned after the then, and that the state of the new promise will be determined by the result of the current THEN.
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject) = >{
if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved') { setTimeout((a)= >{ onResolved(self.data) }) }else{ setTimeout((a)= >{ onResolved(self.data) }) } }) } Copy the code
When the promise is in the Resolved state, the second determination statement is executed when the THEN statement is executed
Then three situations will occur when the second judgment statement is currently executed
- If the callback in then does not return a promise, the new promise that returns is in the resolved state and value is the value returned. Such as:
/ / case 5
let promise = new Promise((resolve,reject) = >{
resolve(1)
})
promise.then( value= >{ return value // Return a value instead of a promise } ) Copy the code
So, we can do this
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject) = >{
if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved') {Modify the code setTimeout((a)= >{ const result = onResolved(self.data) if (result instanceof Promise) { } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }) }else{ setTimeout((a)= >{ onResolved(self.data) }) } }) } Copy the code
A quick explanation:
The onResolved(self.data) callback function is executed as shown in the example below
value=>{
return value // Return a value instead of a promise
}
Copy the code
So this callback function returns value. The resolve function changes the state of the current promise to resolved and saves the value in the data of the current promise.
- If the callback returns a promise, the result of the return promise is the result of that promise, and as the code shows, we return a new promise. If the promise executes resolve, the new promise that is returned is in resolved state. Otherwise it is rejected
/ / case 6
let promise = new Promise((resolve,reject) = >{
resolve(1)
})
promise.then( value= >{ return new Promise((resolve,reject) = >{ resolve(2) / / or //reject(error) }) } ) Copy the code
So we can do this
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject) = >{
if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved') { setTimeout((a)= >{ const result = onResolved(self.data) if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise result.then( value= > {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }) }else{ setTimeout((a)= >{ onResolved(self.data) }) } }) } Copy the code
Here’s the explanation:
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
Copy the code
Since we executed the then in Example 6
value=>{
return new Promise((resolve,reject) = >{
resolve(2)
/ / or
//reject(error)
}) } Copy the code
Return a Promise object that can be in the Resolved state (resolve(2)) or rejected (Reject (Error)).
This causes value => {resolve(value)}, the callback function to be executed, or reason => {reject(Reason)} to be executed.
Therefore, it sets the data of the promise to be returned to value or reason. It will set the status to Resolved or Rejected.
- If this code throws an error and the promise is rejected, we can use try catch to implement it
setTimeout((a)= >{
try{
const result = onResolved(self.data)
if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise
result.then( value= > {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e) } }) Copy the code
Status === ‘rejected’
setTimeout((a)= >{
try{
const result = onRejected(self.data)
if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise
result.then( value= > {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e) } }) Copy the code
Resolve: onResolved(self. Data) and onRejected(self. Data
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
Copy the code
to
if(self.status === 'pending') {// Promise the current state or pending state, save the callback function
self.callbacks.push({
onResolved(){
try{
const result = onResolved(self.data) if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise result.then( value= > {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e) } }, Copy the code
At this point, we find that there is so much of the same code that it is necessary to encapsulate it
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. If the callback returns a promise,returnThe result of the promise is the result of the promise result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the callback does not return a promise,returnThe state of promise is Resolved and value is the returned value. resolve(result) } }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e) } } Copy the code
So since relaxed a lot of
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject) = >{
/ *Calls the processing of the specified callback function, based on the result of execution. Change the promise state of return* / function handle(callback) { try{ const result = callback(self.data) if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise result.then( value= > {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e) } } if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){ handle(onResolved) }, onRejected(){ handle(onRejected) } }) }else if(self.status === 'resolved') { setTimeout((a)= >{ handle(onResolved) }) }else{ // when status === 'rejected' setTimeout((a)= >{ handle(onRejected) }) } }) } Copy the code
In addition, we also know that a promise will occur value passthrough, for example
let promsie = new Promise((resolve,reject) = >{
resolve(1)
})
promsie
.then(2) .then(3) .then(value= >console.log(value)) Copy the code
Running result: 1
If a.then or.catch argument is expected to be a function, passing a non-function will cause value penetration. Value passthrough means that the THEN is invalid if the then is passed to a function other than a function. In practice, when a function is not passed in the then, the promise returned by the then will hold the previous promise.data. This is where value penetration occurs. And every promise returned from an invalid THEN is in the resolved state.
Therefore, to implement the direct transmission through feature, we can do this
Add these sentences to determine whether value passthrough should occur
onResolved = typeof onResolved === 'function'? onResolved: value= > value
onRejected = typeof onRejected === 'function'? onRejected: reason= > {throw reason}
Copy the code
It’s essentially rewriting, if it’s not a function, ignore that value and write another function. The result of this function is to return the data of the previous promise
Promise.prototype.then = function(onResolved,onRejected){
onResolved = typeof onResolved === 'function'? onResolved: value= > value
onRejected = typeof onRejected === 'function'? onRejected: reason= > {throw reason}
var self = this
return new Promise((resolve,reject) = >{ / *Calls the processing of the specified callback function, based on the result of execution. Change the promise state of return* / function handle(callback) { try{ const result = callback(self.data) if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise result.then( value= > {resolve(value)}, reason => {reject(reason)} ) } else { // 1. If the return promise is in the resolved state and is not a promise, value is the value returned. resolve(result) } }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e) } } if(self.status === 'pending') { // Promise the current state or pending state, save the callback function self.callbacks.push({ onResolved(){ handle(onResolved) }, onRejected(){ handle(onRejected) } }) }else if(self.status === 'resolved') { setTimeout((a)= >{ handle(onResolved) }) }else{ // when status === 'rejected' setTimeout((a)= >{ handle(onRejected) }) } }) } Copy the code
3. Implement the catch method
The catch function is the same as the second song callback function in then, so we can do this
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected)
}
Copy the code
Oh, my god. It’s so easy
4. Realize the Promise. Resolve
As we all know, the promise. resolve method can pass three values
- Not a promise
- The promise of success
- Failure state promise
Promise.resolve(1)
Promise.resolve(Promise.resolve(1))
Promise.resolve(Promise.reject(1))
Copy the code
It’s actually a little bit like implementing the THEN above
Promise.resolve = function(value){
return new Promise((resolve,reject) = >{
if (value instanceof Promise) { // If the value is a promise
value.then(
value= > {resolve(value)}, reason => {reject(reason)} ) } else{ // If the value is not a promise resolve(value) } } } Copy the code
5. Realize the Promise. Reject
Implementing this is easy, just return a promise with the status Rejected
/ *The REJECT method of the Promise function objectReturn a promise object with the failure status specified by reason* /
Promise.reject = function(reason){
return new Promise((resolve,reject) = >{ reject(reason) }) } Copy the code
6. Realize the Promise. All
As we know, this method returns a promise
/ *The all method of the Promise function objectReturn a Promise object. The returned promise state succeeds only if all promises are successful* /
Promise.all = function(promises){
return new Promise((resolve,reject) = >{ }) } Copy the code
The state of the promise is determined by the result of traversing each promise
/ *The all method of the Promise function objectReturn a Promise object. The returned promise state succeeds only if all promises are successful* /
Promise.all = function(promises){
return new Promise((resolve,reject) = >{ // Iterate over promises to get the result of each promise promises.forEach((p,index) = >{ }) }) } Copy the code
There are two outcomes:
- If a promise is rejected, the promise state is rejected
Promise.all = function(promises){
return new Promise((resolve,reject) = >{
// Iterate over promises to get the result of each promise
promises.forEach((p,index) = >{
p.then(
value= > { }, reason => { // As long as there is a failure, the return promise state is reject reject(reason) } ) }) }) } Copy the code
- If all the promises traversed are in the Resolved state, the returned promise is in the Resolved state and the resulting value for each promise is passed on
Promise.all = function(promises){
const values = new Array(promises.length)
var resolvedCount = 0 // Count the number of promises in the resolved state
return new Promise((resolve,reject) = >{
// Iterate over promises to get the result of each promise
promises.forEach((p,index) = >{ p.then( value= > { // the state of p is resolved and the value is saved values[index] = value resolvedCount++; // If all ps are resolved, the return promise state is resolved if(resolvedCount === promises.length){ resolve(values) } }, reason => { // As long as there is a failure, the return promise state is reject reject(reason) } ) }) }) } Copy the code
It looks like that, but there’s still a problem here, which is that the array all passes in doesn’t have to be all Promise objects, so maybe it is
All (a/p, 2, 3, p)Copy the code
So you need to wrap a number that isn’t a Promise as a promise
Promise.all = function(promises){
const values = new Array(promises.length)
var resolvedCount = 0 // Count the number of promises in the resolved state
return new Promise((resolve,reject) = >{
// Iterate over promises to get the result of each promise
promises.forEach((p,index) = >{ Promise.resolve(p).then( value= > { // the state of p is resolved and the value is saved values[index] = value resolvedCount++; // If all ps are resolved, the return promise state is resolved if(resolvedCount === promises.length){ resolve(values) } }, reason => { // As long as there is a failure, the return promise state is reject reject(reason) } ) }) }) } Copy the code
7. Realize the Promise. Race
This method is much simpler to implement than all
/ *The RACE method of the Promise function objectReturn a Promise object whose state is determined by the first completed promise* /
Promise.race = function(promises){
return new Promise((resolve,reject) = >{ // Iterate over promises to get the result of each promise promises.forEach((p,index) = >{ Promise.resolve(p).then( value= > { // As long as there is a success, return the promise of the state nine tail resolved resolve(value) }, reason => { // As long as there is a failure, the return promise state is reject reject(reason) } ) }) }) } Copy the code
If you have any questions or suggestions, please comment below. Project source code already in github:https://github.com/Sunny-lucking/howToBuildMyPromise conveniently point a star
So let’s do it, let’s see if we’ve got it? Do 45 Promise interview questions at a time (1.1w)
There may be some problems that you can’t do before, but when you finish reading this article, you can do it again, and find it is as easy as a palm.
This article was typeset using MDNICE