Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.
This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money
Promise core logic implementation
The goal of this article is to understand the principle of Promise by breaking it down a little bit, so that we can achieve the purpose of handwritten Promise
Need to sort out
Before we hand off the code, we need to sort out how to use the Promise, some features of the Promise, convenient for us to better code writing, so we have to know what is the Promise!
1. CallPromise
To invoke a Promise, we need to create a Promise object, starting with the new keyword
new Promise(a)Copy the code
From this code we can see that Promise is a class
2. The parameters
We need to pass in a callback function called an executor that will execute immediately, meaning that when we execute a new Promise(fn), the fn in the function will be called immediately
new Promise(() = >{})Copy the code
3. Actuator parameters
When we pass a callback, we get resolve and reject, which are essentially two functions that change the Promise state.
new Promise((resolve,reject) = >{})Copy the code
4.Promise
The state of the
There are three states in Promise, which are:
- successfulfulfilled
- failurerejected
- Waiting for thepending
5.Promise
State change characteristics
A Promise can only change its state in one of two ways:
- Waiting for the transition to success (
pending => fulfilled
) - Waiting for the transition to failure (
pending => rejected
)
[Note:] Once the status is determined, it cannot be changed
6. How can I modify itPromise
The state? Passing successful or failed values?
You can modify the resolve and reject state. Resolve:
new Promise((resolve,reject) = > {
resolve('success')})Copy the code
Reject:
new Promise((resolve,reject) = >{reject(reject)}Copy the code
7. Then method
Here’s a link: promise.prototype.then () take a look at this link and let’s sort out the requirements
The then() method returns a Promise (en-us). It needs at most two arguments: a callback to Promise’s success and failure cases.
That is, what the then method does internally is determine the state, call different functions depending on the state, and each Promise can call the THEN method, which means that the method is on the prototype chain
let promise = new Promise((resolve,reject) = >{reject(reject)}) promise.() = >{}, () = >{})
Copy the code
8. Callback parameters for then
Let’s start with the following code
let promise = new Promise((resolve,reject) = >{reject(reject)}// promise.then(onFulfilled[, onRejected]);
promise.then(value= > { // fulfillment }, reason => { // rejection});
Copy the code
Based on the link to the document I posted earlier, we can conclude that the success and failure callbacks of then have a success/failure value as an argument
Need to sort out
To sort out our requirements:
Promise
Is a class. To execute this class, we need to pass in a callback function that will execute immediatelyPromise
The callback function in theresolve
withreject
, these two arguments are actually two functionsPromise
There are three states respectively:- successfulfulfilled
- failurerejected
- Waiting for thepending
Promise
Only throughresolve
withreject
State changes are made and data is passed through these two functionsPromise
Once completed, the state change cannot be modified andPromise
There can only be two cases of state change:- Waiting for the transition to success (
pending => fulfilled
) - Waiting for the transition to failure (
pending => rejected
)
- Waiting for the transition to success (
Promise
thethen
Methods call different functions depending on the state and all methods are on the prototype chainPromise
thethen
Method success and failure callbacks, each with a success/failure value as an argument
Begin to write
1. Promise
Is a class
It was a simple first step, a small step for Promise, a giant step for me as a crayon programmer
class MyPromise {}Copy the code
2. You need to pass in a callback function
We receive this using constructor, a special method for creating and initializing objects created by the class.
class MyPromise {
constructor(callback){}}Copy the code
3. The callback function is executed immediately
class MyPromise {
constructor(callback) {
callback()
}
}
Copy the code
4. Promise
The callback function has two arguments to the functionresolve
withreject
Here we need to pass resolve and reject into the callback and define resolve and Reject functions in MyPromise, which we define as arrow functions
class MyPromise {... resolve =() = > {
}
reject = () = >{}}Copy the code
Q: A:So let’s remember how do we call these two functions
new Promise((resolve,reject) = > {
resolve('success')})Copy the code
or
new Promise((resolve,reject) = >{reject(reject)}Copy the code
Is it always called directly like this? If this function is a normal function, does this refer to window or undefined?
Let’s experiment
Ordinary functions:
Arrow function: Here’s a review:
Normal functions have their this pointer determined when called, whereas arrow functions have their this pointer determined when defined!
5.Promise
There are three states in
We need to define the constant of the state, the variable of the state value to change and make it equal to pending by default
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {... }Copy the code
Q: What are the benefits of defining constants?
A: ahem! Mainly for code reuse (vibrato)
6.Promise
throughresolve
withreject
State changes are made and data is passed through these two functions
This is where we need to modify resolve and Reject
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
/ / Promise
status = PENDING
// Success value
value = undefined
// Failed value
reason = undefined
constructor(callback) {
callback(this.resolve,this.reject)
}
resolve = value= > {
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
}
reject = value= > {
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
}
}
Copy the code
7.Promise
There can only be two cases of state change in the
Here, we mainly modify resolve and reject. We need to determine the current status of status and whether it is waiting. If it is in the modified state, if it is not, it cannot be modified
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {... resolve =() = > {
// If the state is not wait, prevent the program from executing down
if(this.status ! == PENDING){return
}
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
}
reject = () = > {
// If the state is not wait, prevent the program from executing down
if(this.status ! == PENDING){return
}
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
}
}
Copy the code
8.Promise
thethen
Methods call different functions depending on the state and all methods are on the prototype chain
Then we need to define the THEN method in the class and determine whether the successful callback is called if the state is successful and the failed callback is called if the state is unsuccessful
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {...then( successCallback, failCallback) {
// If the status is successful
if(this.status === FULFILLED){
successCallback()
}else if(this.status === REJECTED){
failCallback()
}
}
}
Copy the code
9.Promise
thethen
Method success and failure callbacks, each with a success/failure value as an argument
We need to pass the success/failure value back in the then
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {...then( successCallback, failCallback) {
// If the status is successful
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}
}
}
Copy the code
This is where our core module is implemented and tested
Promise core logic implementation summary
On the blackboard On the blackboard On the blackboard
Promise
Is a class. To execute this class, we need to pass in a callback function that will execute immediatelyPromise
The callback function in theresolve
withreject
, these two arguments are actually two functionsPromise
There are three states respectively:- successfulfulfilled
- failurerejected
- Waiting for thepending
Promise
Only throughresolve
withreject
Change the state and save the state with these two valuesPromise
Once completed, the state change cannot be modified andPromise
There can only be two cases of state change:- Waiting for the transition to success (
pending => fulfilled
) - Waiting for the transition to failure (
pending => rejected
)
- Waiting for the transition to success (
Promise
thethen
Methods call different functions depending on the state and all methods are on the prototype chainPromise
thethen
Method success and failure callbacks, each with a success/failure value as an argument
The complete code
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
/ / Promise
status = PENDING
// Success value
value = undefined
// Failed value
reason = undefined
constructor(callback) {
callback(this.resolve,this.reject)
}
resolve = value= > {
// If the state is not wait, prevent the program from executing down
if(this.status ! == PENDING){return
}
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
}
reject = value= > {
// If the state is not wait, prevent the program from executing down
if(this.status ! == PENDING){return
}
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
}
then( successCallback, failCallback) {
// If the status is successful
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}
}
}
Copy the code
Promise
Add asynchronous logic
As we all know, in Promise, we mostly solved some of the problems of making asynchronous calls, but, as far as our code is concerned, there are still some problems with using asynchro
First of all, according to the requirements of our last article:
Promise is a class that, when implemented, passes in a callback function that executes immediately
Then look at the following implementation
let promise1 = new MyPromise((resolve,reject) = > {
setTimeOut(() = >{
resolve(1)},2000)})Copy the code
Our callback function will execute immediately, but the resolve method to change the state is included in the asynchronous task, causing our THEN module to call into confusion.
then( successCallback, failCallback) {
// If the status is successful
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}
}
Copy the code
The then module’s current mood
So we’re going to do a pairthen
Modification of method
Modified then method
- We need to determine what happens when we add waits
- Solution: Add an else judgment
- In the case of waiting, we want to temporarily store the failed/successful callback
- Solution: Add an instance property to store successful/failed callbacks
Code implementation
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {...// Successful callback
successCallback = undefined
// Failed callback
failCallback = undefined.then( successCallback, failCallback) {
// If the status is successful
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}else{
// The waiting condition
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
Copy the code
But it does not solve the problem of asynchronous function execution.
Keep reading!
A callback that completes asynchronous execution
In async, we still execute resolve and Reject, so we just call our saved methods in resolve and Reject
Code implementation
resolve = value= > {
// If the state is not wait, prevent the program from executing down
if(this.status ! == PENDING){return
}
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
// Determine if the successful callback exists, and if so, call
this.successCallback && this.successCallback(this.value)
}
reject = value= > {
// If the state is not wait, prevent the program from executing down
if(this.status ! == PENDING){return
}
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
// Determine if the failed callback exists, and if so, invoke
this.failCallback && this.failCallback(this.reason)
}
Copy the code
Ok, so we’re done here
Test the
const MyPromise = require('./myPromise')
let promise1 = new MyPromise((resolve,reject) = > {
setTimeout(() = >{
resolve('success')
})
})
promise1.then(value= >{
console.log('success',value)
},reasons= > {
console.log('failure',reasons)
})
Copy the code
Yes, no problem
This completes the processing of asynchronous functions and moves on to the next chapter
The complete code
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
/ / Promise
status = PENDING
// Success value
value = undefined
// Failed value
reason = undefined
// Successful callback
successCallback = undefined
// Failed callback
failCallback = undefined
constructor (callback) {
callback(this.resolve, this.reject)
}
resolve = value= > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return
}
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
// Determine if the successful callback exists, and if so, call
this.successCallback && this.successCallback(this.value)
}
reject = value= > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return
}
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
// Determine if the failed callback exists, and if so, invoke
this.failCallback && this.failCallback(this.reason)
}
then (successCallback, failCallback) {
// If the status is successful
if (this.status === FULFILLED) {
console.log('test')
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
Copy the code
Promise
Implement the then method multiple calls to add multiple handlers
Ask questions
Now there is a new problem, as in the code below, we can execute promise1 multiple times in asynchronous mode, and output it multiple times
The test code
let promise1 = new Promise((resolve,reject) = > {
setTimeout(() = >{
resolve('success')},2000)})/ / for the first time
promise1.then(value= >{
console.log('success',value)
},reasons= > {
console.log('failure',reasons)
})
/ / the second time
promise1.then(value= >{
console.log('success',value)
},reasons= > {
console.log('failure',reasons)
})
/ / the third time
promise1.then(value= >{
console.log('success',value)
},reasons= > {
console.log('failure',reasons)
})
Copy the code
Write a classmyPromise
test
The class we implemented couldn’t do it! How to do?
What difficulties we encounter, don’t be afraid, smile in the face of it, the best way to eliminate fear is to face fear, persistence is victory, come on, Ollie give!
Find the problem
First of all, let’s address the problem, why do we only execute once? After we looked through the code, we found the cause
then (successCallback, failCallback) {
// If the status is successful
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback = successCallback
this.failCallback = failCallback
}
}
Copy the code
Why is this happening
First, we don’t store all the time we call then, so if we call it asynchronously, successCallback and failCallback will store the value of the last time we did it. As a result, other values are not executed, as shown below:
Ps:Synchronization and no problem, you can test it
The solution to the problem
Locating the root of the disease, then our solution is very convenient:
- transform
then
The storage of the failed/successful methods of the function => can be changed to an array for storage - transform
resolve
withreject
对successCallback
andfailCallback
The execution mode => loop over the stored array and then execute
transformthen
function
SuccessCallback and failCallback are changed to arrays
class MyPromise {...// Successful callback
successCallback = []
// Failed callback
failCallback = []
...
then (successCallback, failCallback) {
// If the status is successful
if (this.status === FULFILLED) {
console.log('test')
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
Copy the code
Ok, here and the modification is complete!
transformresolve
withreject
function
Resolve:
resolve = value= > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return
}
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
// Determine if the successful callback exists, and if so, call
// this.successCallback && this.successCallback(this.value)
while(this.successCallback.length){
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.successCallback.shift()(this.value)
}
}
Copy the code
Reject:
reject = value= > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return
}
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
// Determine if the failed callback exists, and if so, invoke
// this.failCallback && this.failCallback(this.reason)
while(this.failCallback.length){
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.failCallback.shift()(this.reason)
}
}
Copy the code
So we’re done here
test
Now that we’re done, how else can we verify that we’re right
The test case
const myPromise = require('./myPromise')
let promise1 = new myPromise((resolve, reject) = > {
setTimeout(() = >{
reject('failure')},1000)
})
promise1.then(value= >{
console.log(value)
},reason= > {
console.log(1)
console.log(reason)
})
promise1.then(value= >{
console.log(value)
},reason= > {
console.log(2)
console.log(reason)
})
promise1.then(value= >{
console.log(value)
},reason= > {
console.log(3)
console.log(reason)
})
Copy the code
The test results
Perfect, other use cases will not take screenshots, too many, you are interested in testing it yourself!
conclusion
The main purpose of this summary is to solve the problem that the then method under Promise can be called multiple times. When its state changes, the internal methods are executed in sequence, and the synchronous and asynchronous cases need to be distinguished
- In the asynchronous case, we need to store methods and call them in turn
- In the case of synchronization, we just need to call the corresponding method based on the state
The complete code
const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
/ / Promise
status = PENDING
// Success value
value = undefined
// Failed value
reason = undefined
// Successful callback
successCallback = []
// Failed callback
failCallback = []
constructor (callback) {
callback(this.resolve, this.reject)
}
resolve = value= > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return
}
// Change the status to success
this.status = FULFILLED
// Save the successful value
this.value = value
// Determine if the successful callback exists, and if so, call
// this.successCallback && this.successCallback(this.value)
while(this.successCallback.length){
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.successCallback.shift()(this.value)
}
}
reject = value= > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return
}
// Change the status to failed
this.status = REJECTED
// Save the failed value
this.reason = value
// Determine if the failed callback exists, and if so, invoke
// this.failCallback && this.failCallback(this.reason)
while(this.failCallback.length){
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.failCallback.shift()(this.reason)
}
}
then (successCallback, failCallback) {
// If the status is successful
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
Copy the code
Promise
implementationthen
Method
Need to sort out
The then methods below Promise can be called chained, and the return value of the next THEN method is actually the return value of the callback function of the previous THEN method. Let’s look at a few cases
1. The first function is called without a return value:
Code:
let promise1 = new Promise((resolve, reject) = > {
resolve('success')
})
promise1.then(val= > {
console.log(val)
}).then(val= >{
console.log(val)
})
Copy the code
Results:
2. The first function to be chained returns a value:
Code:
let promise1 = new Promise((resolve, reject) = > {
resolve('success')
})
promise1.then(val= > {
console.log(val)
return 100
}).then(val= >{
console.log(val)
})
Copy the code
Results:
So you can be sure that the return value of the next THEN method is actually the return value of the callback function of the previous THEN method
So we need to do two things:
then
Method- Pass the return value of the previous one to the next
then
methods
implementationthen
Method
First, we need to make it clear that then is under a Promise. If we want to implement chained calls to then methods, each then method should return a Promise object. If then returns a Promise object, then we can make chained calls easier.
The first step
Since we’re returning a Promise, we return a Promise in the then method
then (successCallback, failCallback) {
let promiseThen = new MyPromise()
// If the status is successful
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
return promiseThen
}
Copy the code
The second step
Now that it has returned, do we need to modify the judgment a little bit? Let’s recall that when we execute a new Promise, we can pass in a callback function that executes immediately!
Well, I have a half-baked idea! Pass our internal then judgments into MyPromise to execute!
then (successCallback, failCallback) {
let promiseThen = new MyPromise(() = > {
// If the status is successful
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return promiseThen
}
Copy the code
At this point our chained call to the THEN method is written
then
Method value passing
First, we need to save the return value of the execution. This is very simple, we just need to save the return value of the execution
let x = successCallback(this.value)
Copy the code
So the next question is, how do you pass the data?
Don’t worry, let’s review again!
Promise uses resolve and Reject to change state, and data is passed through these two functions
We can then pass the return value to the promise data we return by calling resolve, which in turn passes the return value of the previous one to the next THEN method
let promiseThen = new MyPromise((resolve, reject) = > {
// If the status is successful
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolve(x)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// The waiting condition
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return promiseThen
Copy the code
Is it over? Don’t have not
At this point our judgment seems to be over, but! We’re not done yet. What if you return a Promise object? What if the object succeeds or fails? Roll up your sleeves and work hard!
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolve(x)
}
Copy the code
Resolve (x) cannot be written this way! I’m going to make some changes, add some judgments, what judgments?
- Check if x is a Promise object
- If you don’t call reslove directly
- If it is a Promise object, the call is selected based on its results
resolve
orreject
function
Considering the reusability of the code, we extract this statement
// Resolve whether the promise function is available
// Instead of calling reslove directly
// Call reslove or reject as appropriate
function resolvePromise(x, resolve, reject){
if(x instanceof MyPromise){
x.then(resolve, reject)
}else{
resolve(x)
}
}
Copy the code
Then modify the then
then (successCallback, failCallback) {
let promiseThen = new MyPromise((resolve, reject) = > {
// If the status is successful
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolvePromise(x, resolve, reject)
}
...
})
return promiseThen
}
Copy the code
This way we have handled the synchronous case, of course we also need to handle the asynchronous case!
The idea is the same
.// The waiting condition
this.successCallback.push(() = > {
try {
let x = successCallback(this.value)
resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.failCallback.push(() = > {
try {
let x = failCallback(this.reason)
resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
Copy the code
Here we add catch to prevent the execution from failing, of course the previous execution also needs to add OK to enter the test mode
test
Test synchronous chain calls
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) = > {
resolve('success')})let promise1 = new myPromise((resolve, reject) = > {
resolve('1' success)
})
promise.then(val= > {
console.log('First time',val)
return promise1
}).then(val= >{
console.log('The second time',val)
return promise
}).then(val= >{
console.log('Third time',val)
return promise1
}).then(val= >{
console.log('The fourth time',val)
})
Copy the code
Expected output:
- First success
- Success the second time 1
- Third time successful
- The fourth success 1
Synchronous chain call test results
Asynchronous chain call tests
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('Successful 1S delay version')},1000)})let promise1 = new myPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('Success 0.5 Delayed version')},500)
})
promise.then(val= > {
console.log('First time',val)
return promise1
}).then(val= >{
console.log('The second time',val)
return promise
}).then(val= >{
console.log('Third time',val)
return promise1
}).then(val= >{
console.log('The fourth time',val)
})
Copy the code
Expected output:
- The first successful 1s delay version
- Second success 0.5 delayed version
- Third successful 1s delay version
- The fourth success 0.5 delayed version
Asynchronous chain call test results
It’s all ok
conclusion
The main reason for the chain call of then is that we return the same class, so that the function can continue to call then methods. We should pay attention to the asynchronous and synchronous call of function when we write, and also pay attention to our method selection.
Promise
thethen
Method chain calls recognize that the Promise object returns itself
We can return an object when we use the then method, but if we return the original Promise, there will be a thing, and the Promise will be called in a loop, and the system will bug, and we want to avoid that, and we want to demonstrate that error
Use systempromise
Object to simulate an error
let promise = new Promise((resolve, reject) = > {
resolve(100)})let promise1 = promise.then((value) = >{
console.log(value)
return promise1
})
Copy the code
The results of
Ok, so that’s where our requirements come in, how do we handle this case of calling ourselves?
implementation
It’s actually pretty simple, so let’s go to the then method, remember our code before? Let x = successCallback(this.value) let x = successCallback(this.value) let x = successCallback(this.value) let x = successCallback(this.value) We choose resolvePromise for this transformation
resolvePromise
The reconstruction of
ResolvePromise adds an entry to thenPromise, and then throws an error if the promise is the same
function resolvePromise (thenPromise,returnValue, resolve, reject) {
if(thenPromise === returnValue){
return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))}if (returnValue instanceof MyPromise) {
returnValue.then(resolve, reject)
} else {
resolve(returnValue)
}
}
Copy the code
Then change the place where the method is called and add a pass parameter.
But the problem is not so easy!
So let’s look at the code here
let thenPromise = new MyPromise((resolve, reject) = >{...let x = successCallback(this.value)
resolvePromise(thenPromise, x, resolve, reject)
...
})
Copy the code
Here we need to pass the thenPromise (resolvePromise, x, resolve, reject), but thenPromise is declared after the new MyPromise has finished. What to do?? Actually also simple! Asynchronous call!! SetTimeout !!!! Did it all come out right away? Start reinventing!
After you change the paste code set
test
The test code
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) = > {
resolve('success')})let promise1 = promise.then(val= > {
console.log(val)
return promise1
})
promise1.then(val= >{},reason= > {
console.log(reason.message)
})
Copy the code
The test results
perfect
then
Method parameters become optional
let promise3 = new Promise((resolve, reject) = > {
resolve('failure')
})
promise3.then().then().then( value= > console.log(value))
Copy the code
A Promise is a case where you can keep passing parameters until there is a callback method
How do you do that? Value => value; value => value; value => value; Perfect utilization characteristic
In the code
// Create a variable
const isFunction = (value) = > typeof value === "function"; . .then(successCallback, failCallback) {
successCallback = isFunction(successCallback)
? successCallback
: (value) = > value;
failCallback = isFunction(failCallback)
? failCallback
: (err) = > {
throwerr; }; . }...Copy the code
Test the
The test case
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('success')},2000)})let promise1 = new myPromise((resolve, reject) = > {
setTimeout(() = > {
reject('failure')},2000)
})
promise.then().then().then( value= > console.log(value,1), value= > console.log(value,2))
promise1.then().then().then( value= > console.log(value,3), value= > console.log(value,4))
Copy the code
Expected output:
- Success 1
- Failure of 4
The test results
The complete code
const PENDING = "pending"; / / wait for
const FULFILLED = "fulfilled"; / / success
const REJECTED = "reject"; / / fail
const isFunction = (value) = > typeof value === "function";
class MyPromise {
/ / Promise
status = PENDING;
// Success value
value = undefined;
// Failed value
reason = undefined;
// Successful callback
successCallback = [];
// Failed callback
failCallback = [];
// Errors need to be caught in the actuator
constructor(callback) {
try {
callback(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve = (value) = > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return;
}
// Change the status to success
this.status = FULFILLED;
// Save the successful value
this.value = value;
// Determine if the successful callback exists, and if so, call
// this.successCallback && this.successCallback(this.value)
while (this.successCallback.length) {
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.successCallback.shift()(); }}; reject =(value) = > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return;
}
// Change the status to failed
this.status = REJECTED;
// Save the failed value
this.reason = value;
// Determine if the failed callback exists, and if so, invoke
// this.failCallback && this.failCallback(this.reason)
while (this.failCallback.length) {
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.failCallback.shift()(); }};then(successCallback, failCallback) {
successCallback = isFunction(successCallback)
? successCallback
: (value) = > value;
failCallback = isFunction(failCallback)
? failCallback
: (err) = > {
throw err;
};
let thenPromise = new MyPromise((resolve, reject) = > {
// If the status is successful
if (this.status === FULFILLED) {
setTimeout(() = > {
try {
let x = successCallback(this.value);
// Determine if x is MyPromise
// If yes, go straight
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0);
// resolve(x)
} else if (this.status === REJECTED) {
setTimeout(() = > {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0);
} else {
// The waiting condition
this.successCallback.push(() = > {
setTimeout(() = > {
try {
let x = successCallback(this.value);
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0);
});
this.failCallback.push(() = > {
setTimeout(() = > {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0); }); }});returnthenPromise; }}// Resolve whether the promise function is available
// Instead of calling reslove directly
// Call reslove or reject as appropriate
function resolvePromise(thenPromise, value, resolve, reject) {
if (thenPromise === value) {
return reject(
new TypeError("Chaining cycle detected for promise #<MyPromise>")); }if (value instanceof MyPromise) {
value.then(resolve, reject);
} else{ resolve(value); }}module.exports = MyPromise;
Copy the code
Promise
theall
methods
All is designed to solve the problem of asynchronous concurrency by allowing us to get the results of asynchronous code execution in the order in which it is called
example
function promise () {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('1' success)},2000)})}function promise1 () {
return new Promise((resolve, reject) = > {
resolve('1' success)})}Copy the code
There are two functions, if we call promise and promise1 at the same time, we get the code for promise1 first and then the promise value, but promise. all gets the execution order of promise and promise1. How does this work?
Let’s look at parameters and return values and so on
parameter
-
可迭代
An iterable, such as Array or String.
The return value
- If the argument passed is an empty iterable, return a Promise that is already resolved.
- If the parameter passed does not contain any
promise
, returns oneAsynchronously ResolvedPromise
. Note: Google Chrome 58 returns one in this caseAlready resolvedThe state of thePromise
. - Otherwise return onePending 的
Promise
. This returnspromise
And then there will be at allpromise
Both completed or have onepromise
When the failureasynchronousTo complete or fail. See below for an example of “asynchrony or synchronization for Promise.all”. The return value will be the same as that in the argumentpromise
Order, not by callpromise
To determine the order of completion.
instructions
This method is useful when gathering the return results of multiple promises.
Fulfillment: If the iterable passed in is empty, promise.all returns a Promise in a completed state synchronously. The promise returned by promise.all becomes completed asynchronously if all incoming promises become complete, or if there are no promises in the incoming iterable. In any case, the result of the completion state of the Promise returned by promise.all is an array containing the values of all the passed iteration parameter objects (including non-Promise values).
Rejection: If a promise is passed in with a failed promise, promise. all asynchronously gives that failed result to the failed state callback, regardless of whether the other promise is fulfilled or not.
The sample
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) = > {
setTimeout(resolve, 100.'foo');
});
Promise.all([p1, p2, p3]).then(values= > {
console.log(values); // [3, 1337, "foo"]
});
Copy the code
implementation
See the use example above, promise.all, so the all method must be a static method
Ok, open the whole
1. Add static methodsall
Add static methods to myPromise and parameters to it (of type array)
static all (array) {
}
Copy the code
2. The return value
All returns a Promise object. All returns a Promise object. So!
static all (array) {
return new MyPromise((resolve, reject) = >{})}Copy the code
3. Determine the type of the value
So if it’s a Promise object, we’ll just put it in the array of the result value, and if it’s a Promise object, we’ll put it in the array of the result value after we execute, okay
static all (array) {
return new MyPromise((resolve, reject) = >{
for(const i in array) {
let current = array[i]
if(current instanceof MyPromise){
/ / MyPromise object
}else {
// Non-myPromise object}}})}Copy the code
4. Store the results
As mentioned above, we need to store the data according to the situation, so we need to store the results in the corresponding order. Fortunately, the array can store the values through the key
static all (array) {
// Store the result
let result = [];
function addData(key, value) {
// Add values in the order they are executed.
result[key] = value
}
return new MyPromise((resolve, reject) = >{
// Why not use the for loop
for(const i in array) {
let current = array[i]
if(current instanceof MyPromise){
/ / MyPromise object
current.then(value= > addData(i, value), reason= > reject(reason))
}else {
// Non-myPromise object
addData(i, current)
}
}
resolve(result)
})
}
Copy the code
But there’s a problem with that! What’s the problem?
It doesn’t support asynchronous operations! Oh, my God! Oh, my god! Too the heart
Why not support asynchronous operations?
Don’t ask, ask is no! Just kidding, the for operation is synchronous, so the asynchronous operation will not execute in time, simple as that
Perfect the code!
We simply add a variable of how many times it has been executed, and then determine based on that variable. If the number of times it has been executed matches the length of our input parameter, we just tell it to execute resolve
static all (array) {
// Store the result
let result = [];
let index = [];
return new MyPromise((resolve, reject) = >{
function addData(key, value) {
// Add values in the order they are executed.
result[key] = value
index++;
if(index === array.length){
resolve(result)
}
}
// Why not use the for loop
for(const i in array) {
let current = array[i]
if(current instanceof MyPromise){
/ / MyPromise object
current.then(value= > addData(i, value), reason= > reject(reason))
}else {
// Non-myPromise object
addData(i, current)
}
}
})
}
Copy the code
test
function promise () {
return new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('success')},2000)})}function promise1 () {
return new MyPromise((resolve, reject) = > {
resolve('1' success)
})
}
MyPromise.all(['a'.'b',promise(),promise1()]).then((res) = > {
console.log(res)
})
Copy the code
Expected output: [‘a’, ‘b’, ‘success ‘,’ success 1′]
The results of
Implementation of the promise.resolve method
The promise.resolve (value) method returns a Promise object resolved with the given value. If the value is a Promise, the promise is returned; If the value is thenable (that is, with the “then” method), the returned promise “follows “the thenable object and adopts its final state; Otherwise the returned promise will be fulfilled with this value. This function flattens out the multiple layers of nesting of promise-like objects.
How do you do that?
Resolve (value) will determine if the value is a Promise object. If it is, it will return the Promise object. If it is not, we need to wrap a Promise object.
In the code
static resolve (value) {
if(value instanceof MyPromise){
/ / MyPromise object
return value
}else {
// Non-myPromise object
return new MyPromise(resolve= > resolve(value))
}
}
Copy the code
test
The test case
function promise () {
return new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('success')},2000)})}function promise1 () {
return new MyPromise((resolve, reject) = > {
resolve('1' success)
})
}
MyPromise.resolve(100).then(res= > console.log(res))
MyPromise.resolve(promise()).then(res= > console.log(res))
MyPromise.resolve(promise1()).then(res= > console.log(res))
Copy the code
Expected output: 100 success 1 success
The test results
Completed!
finally
Method implementation
The finally method has two characteristics
- No matter
promise
Whether the object status succeededfinally
The callback function is executed finally
You can call it chainedthen
Method to get the return value
How do you do that?
First we can make sure that finally is not a static method, so we go into the file
finally(callback){}Copy the code
Then what should I do?? Implement it bit by bit, as required
No matterpromise
Whether the object status succeededfinally
The callback function is executed
If we call finally, we can also call then inside the function.
Remember the two arguments to then? We pass in and call our method at the same time!
finally(callback){
this.then(() = >{
callback()
},() = >{
callback()
})
};
Copy the code
Perfect solution!
finally
You can call it chainedthen
Method to get the return value
We’ve talked about this a couple of times, we can chain call then and that’s just returning a promise object, right? So if we think about it then it looks like we’re returning this object so why can’t we return this then?
finally(callback){
return this.then(() = >{
callback()
},() = >{
callback()
})
};
Copy the code
But that doesn’t seem right! What if the chain call does not get arguments? Don’t panic! A little in the transformation of some good!
finally(callback){
return this.then(value= >{
callback()
return value
},reason= >{
callback()
throw reason
})
};
Copy the code
A problem has arisen
What’s going on?
Look at this test code:
function promise () {
return new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('promise success')},1000)})}function promise1 () {
return new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('promise1 success')},1000)
})
}
promise().finally(() = > {
console.log('promise finally')
return promise1()
}).then(value= > {
console.log(value)
},reason= > {
console.log(reason)
})
Copy the code
The expectation is to execute the promise1 function first and then, but this is not done
What to do? (THE GIF cannot be captured)
It occurred to me that it would be nice if we could wait for this method to be executed later. Depending on whether our execution method is being processed asynchronously and converted to a Promise object, then we can make subsequent chain calls and execute our method.
The resolve(value) method returns a resolved Promise object with the given value!!!! Then we can use it to modify the code!
To test
This test is ok, I don’t know why my GIF can not be captured, you will see my description!
There is no problem with the test result, it was executed after 1s, there is no problem!
Implementation of the catch method
The catch method is a function designed to handle the failure of a Promise method, so that you don’t have to pass a failed callback to the then method
implementation
catch (failCallback) {
return this.then(undefined, failCallback)
};
Copy the code
It’s that simple!
Test the
The test code
const MyPromise = require('./myPromise')
function promise () {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('promise success')},1000)})}function promise1 () {
return new MyPromise((resolve, reject) = > {
setTimeout(() = > {
reject('promise1 failure')},1000)
})
}
promise()
.then(val= > console.log(val))
.catch(res= > console.log(res))
promise1()
.then(val= > console.log(val))
.catch(res= > console.log(res))
Copy the code
The results of
summary
And that’s where our handwritten code ends
The complete code
const PENDING = "pending"; / / wait for
const FULFILLED = "fulfilled"; / / success
const REJECTED = "reject"; / / fail
const isFunction = (value) = > typeof value === "function";
class MyPromise {
/ / Promise
status = PENDING;
// Success value
value = undefined;
// Failed value
reason = undefined;
// Successful callback
successCallback = [];
// Failed callback
failCallback = [];
// Errors need to be caught in the actuator
constructor (callback) {
try {
callback(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve = (value) = > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return;
}
// Change the status to success
this.status = FULFILLED;
// Save the successful value
this.value = value;
// Determine if the successful callback exists, and if so, call
// this.successCallback && this.successCallback(this.value)
while (this.successCallback.length) {
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.successCallback.shift()(); }}; reject =(value) = > {
// If the state is not wait, prevent the program from executing down
if (this.status ! == PENDING) {return;
}
// Change the status to failed
this.status = REJECTED;
// Save the failed value
this.reason = value;
// Determine if the failed callback exists, and if so, invoke
// this.failCallback && this.failCallback(this.reason)
while (this.failCallback.length) {
// Call the Shift method and each time it executes, push the method out of the array stack and return the data back
this.failCallback.shift()(); }}; then (successCallback, failCallback) { successCallback = isFunction(successCallback) ? successCallback :(value) = > value;
failCallback = isFunction(failCallback)
? failCallback
: (err) = > {
throw err;
};
let thenPromise = new MyPromise((resolve, reject) = > {
// If the status is successful
if (this.status === FULFILLED) {
setTimeout(() = > {
try {
let x = successCallback(this.value);
// Determine if x is MyPromise
// If yes, go straight
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0);
// resolve(x)
} else if (this.status === REJECTED) {
setTimeout(() = > {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0);
} else {
// The waiting condition
this.successCallback.push(() = > {
setTimeout(() = > {
try {
let x = successCallback(this.value);
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0);
});
this.failCallback.push(() = > {
setTimeout(() = > {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch(e) { reject(e); }},0); }); }});return thenPromise;
};
finally (callback) {
return this.then(value= > {
return MyPromise.resolve(callback()).then(() = > value)
}, reason= > {
return MyPromise.resolve(callback()).then(() = > { throw reason })
})
};
catch (failCallback) {
return this.then(undefined, failCallback)
};
static all (array) {
// Store the result
let result = [];
let index = [];
return new MyPromise((resolve, reject) = > {
function addData (key, value) {
// Add values in the order they are executed.
result[key] = value
index++;
if (index === array.length) {
resolve(result)
}
}
// Why not use the for loop
for (const i in array) {
let current = array[i]
if (current instanceof MyPromise) {
/ / MyPromise object
current.then(value= > addData(i, value), reason= > reject(reason))
} else {
// Non-myPromise object
addData(i, current)
}
}
})
};
static resolve (value) {
if (value instanceof MyPromise) {
/ / MyPromise object
return value
} else {
// Non-myPromise object
return new MyPromise(resolve= > resolve(value))
}
}
}
// Resolve whether the promise function is available
// Instead of calling reslove directly
// Call reslove or reject as appropriate
function resolvePromise (thenPromise, value, resolve, reject) {
if (thenPromise === value) {
return reject(
new TypeError("Chaining cycle detected for promise #<MyPromise>")); }if (value instanceof MyPromise) {
value.then(resolve, reject);
} else{ resolve(value); }}module.exports = MyPromise;
Copy the code
See here, give it a thumbs up!