First, the realization of Promise core logic
You need to know enough about promises before you can implement their principles.
Analyze a wave:
- A Promise is a class that passes an executor (function) to a new Promise instance, and the executor executes immediately.
- A Promise has two instance methods: resolve and Reject, which are passed in as arguments to the executor function to change the state of the Promise.
- Promise has three states, which are: successful state -> depressing, failure state -> Rejected, wait state -> Pending. Promise defaults to a wait state. The state is changed through the resolve method, which is used to change the state from pending to DEPRESSING, and the Reject method, which is used to change the state from pending to Rejected. Once the state has changed, it cannot be changed.
- Promise also has a prototype THEN method, which accepts two callback functions, one which is a pity (successful state) and the other which is a pity (failed state). The two callback functions accept the value passed by resolve and reject, respectively.
- The THEN method can also make chained calls, indicating that the then method returns a promise object, and subsequent THEN methods get the value returned by the previous THEN method.
1. Promise handles synchronization code
// Based on the above analysis, we can get the following code
// 2. Define the three states of Promise
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
// 1. The executor executes immediately, accepting two instance methods
constructor(executor) {
executor(this.resolve, this.reject)
}
// Instance attribute definition
// The promise state defaults to wait
status = PENDING
// Success status value
value = null
// Failed status value
reason = null
// The arrow function must be used to bind its this to the instance
resolve = (value) = > {
// 3. The resolve method makes the promise state a successful state and cannot change it again.
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
}
reject = (reason) = > {
// 4. Reject makes the promise state a failed state that cannot be changed again.
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
}
// Define the prototype then method
then(successCallback, faildCallback) {
// 5. Determine the state of the promise. If it is a successful state, call the successful callback function; if it is a failed state, call the failed callback function
if (this.status === FULFILLED) {
// The value of the callback function that needs to be saved when the resolve method is called
successCallback(this.value)
} else if (this.status === REJECTED) {
// The value of the callback function, which needs to be saved when the Rejected method is called
faildCallback(this.reason)
}
}
}
// Test a wave
const promise = new MyPromise((resolve, reject) = > {
// resolve('hello reborn jiang')
// reject('hello reborn jiang')
setTimeout(() = > {
resolve('hello reborn jiang')},0);
})
promise.then(value= > {
console.log(value, 'Value of success status') // hello reborn jiang success status value
}, reason= > {
console.log(reason, 'Value of failed state') // Hello reborn jiang failed status value
})
Copy the code
Why is there no printed message when there is asynchronous code in an executor function? Explanation: When an executor function has asynchronous code in it, the callback is no longer executed when the then method is called. As a result, resolve and Reject methods have not been called to change the promise state when the THEN method is executed. Therefore, the state is still pending, but pending state is not processed in the THEN method. So it’s not printed.
From the above analysis, we can conclude that if the then method is in a pending state, the code in the executor must be an asynchronous task
The above explanation involves the JS execution mechanism. Simply speaking, this is because the asynchronous code in the executor is handed to the thread of the browser to handle the asynchronous task. When the asynchronous task is completed, it will be added to the message queue, and when the call stack code is finished, the EventLoop will fetch the task from the message queue for execution. If the JS implementation mechanism is not very understanding of the friends, you can see the author of another article on JS implementation mechanism ha ~
2. Promise handles asynchronous code
Analyze a wave:
- The then method proves that the code in the executor is an asynchronous task if the state is still pending.
- If you call the then callbacks directly in pending state, you will not get resolve or Reject.
- So we store the callbacks in pending state and call the success or failure callbacks of then when resolve or Reject executes.
// Based on the above analysis we can get the following code
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
// 2. Define two variables to store successful or failed callbacks
successCallback = null
faildCallback = null
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
// 3. Call resolve again when the resolve method is executed.
this.successCallback && this.successCallback(value)
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
// 3. Call resolve again when the resolve method is executed
this.faildCallback && this.faildCallback(reason)
}
then(successCallback, faildCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
faildCallback(this.reason)
} else {
// Pengding proves that executor has asynchronous tasks
// 1. Store the callback to be called again when resolve, reject is executed
console.log('Oh time asynchronous task, code in Pending executes')
this.successCallback = successCallback
this.faildCallback = faildCallback
}
}
}
// Test a wave
const promise = new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('hello reborn jiang')},0);
})
promise.then(value= > {
console.log(value, 'Value of success status')},reason= > {
console.log(reason, 'Value of failed state')})// Print the following result:
// Async task, pending code executes
// hello reborn jiang success status value
Copy the code
Summary: Ha ha ha, you are good. You have implemented a simplified version of promise that can be used to handle synchronous and asynchronous tasks.
Second, the further implementation of then method function
1. The THEN method of a Promise instance can be called multiple times
The THEN method of a Promise instance can be called multiple times to handle an asynchronous task, and the callbacks in each THEN method it calls are executed.
Promise (example) :
//
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('hello reborn jiang')},0)
})
promise.then(res= > {
console.log(res, 'First call')
})
promise.then(res= > {
console.log(res, 'Second call')
})
promise.then(res= > {
console.log(res, 'Third call')})// Console results are as follows:
// Hello reborn jiang is first called
// Hello reborn jiang is called for the second time
// Hello Reborn jiang for the third time
Copy the code
MyPromise (example) :
const promise = new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('hello world')},0);
})
promise.then((value) = > {
console.log(value, 'Value of success status 1')
})
promise.then((value) = > {
console.log(value, 'Success status value 2')
})
promise.then((value) = > {
console.log(value, 'Success status value 3')})// Print the following:
// Async task, pending code executes
// Async task, pending code executes
// Async task, pending code executes
// Hello world success status value 3
Copy the code
Conclusion is as follows: because this is the case in the treatment of the asynchronous task, then methods are executed first, then methods of pending conditions stored in the callback method is just a variable, which leads to cannot have all = callback function to save, so then the final execution will cover at the front of the callback function.
MyPromise (modified version) :
// Based on the above analysis we can get the following code
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
// successCallback = null
// faildCallback = null
// 1. The variable used to store the callback function needs to be an Array to ensure that each callback can be stored.
successCallbackList = []
faildCallbackList = []
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
// this.successCallback && this.successCallback(value)
// 3. Wait for the asynchronous task callback to execute, then loop through the callback function execution.
while (this.successCallbackList.length)this.successCallbackList.shift()(value)
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
// this.faildCallback && this.faildCallback(reason)
// 3. Wait for the asynchronous task callback to execute, then loop through the callback function execution.
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
faildCallback(this.reason)
} else {
// this.successCallback = successCallback
// this.faildCallback = faildCallback
// 2. Store each callback into an array
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
}
}
// Test a wave
const promise = new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('hello reborn jiang')},0)
})
promise.then((value) = > {
console.log(value, 'Value of success status 1')
})
promise.then((value) = > {
console.log(value, 'Success status value 2')
})
promise.then((value) = > {
console.log(value, 'Success status value 3')})// Print the result
// hello reborn jiang success status value 1
// hello reborn jiang success status value 2
// hello reborn jiang success status value 3
Copy the code
2. Chain calls to the then method
Analyze a wave:
- The then method can make a chain call to prove that it returns a new Promise object internally
- The then callback returns a normal value and calls resolve of the newly created Promise object to store the value on the newly created instance. If the then method returns a promise object, we need to know whether the state of the promise is successful or failed, and if it succeeds, we call resolve, and if it fails, we call Reject. (Note the case of normal values)
- Normal value: The return value of the successful callback (or failed callback) of the previous THEN method is used as an argument to the successful callback of the later THEN method
The Promise code looks like this (sample)
// The previous two points summarize the sample code
const promise = new Promise((resolve, reject) = > {
resolve('hello reborn')
})
promise.then(value= > {
console.log(value, 'First then')
return 'hello David'
}).then(value= > {
console.log(value, 'Second then')},// Print the following
// Hello reborn then
// Hello David second then
// The second point explains the failed callback case
const promise = new Promise((resolve, reject) = > {
reject('hello reborn')
})
promise.then(() = > {},
reason= > {
console.log(reason, 'Failed callback')
return 'errorr'
}).then(value= > {
console.log(value, 'Successful callback')},reason= > {
console.log(reason, 'Failure back')})// Print the following
// Hello reborn failed callback
Errorr successfully callback
// The then callback returns the case of the Promise object
const promise = new Promise((resolve, reject) = > {
resolve('hello reborn')
})
promise.then(value= > {
console.log(value, 'First then execution')
return new Promise((resolve, reject) = > {
resolve('success to SecondThen')
})
}).then(value= > {
console.log(value, 'The second then method executes')})// Print the result
// Hello reborn first then execution
// The success to SecondThen method is executed
Copy the code
MyPromise code:
// Based on the above analysis we can get the following code
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()(value)
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
// 1. Chained calls are supported, and a new promise must be returned (this cannot be returned because old promises may contain state values, callback functions list, etc.)
const promise2 = new MyPromise((resolve, reject) = > {
if (this.status === FULFILLED) {
// 2. Determine whether the current Promise object returns a normal value or a promise object
// Ordinary values call the resolve method directly to pass the value to the next then method
// If the then method returns a PROMISE, determine the state of the returned Promise
// If the state is successful, call the resolve method of promise2 to pass the value of the promise returned by then to the next THEN
// If the state is a failure, call the reject method of promise2 to pass the failure value of the promise returned by then to the second argument of the next THEN
const successCallBackValue = successCallback(this.value)
This is a big pity. // 3. This is a big pity, unless // PENDING
resolvePromise(successCallBackValue, resolve, reject)
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
}
function resolvePromise(returnVal, resolve, reject) {
// 4
if (returnVal instanceof MyPromise) {
// 6. The promise object, after checking the status, calls resolve or reject
// returnVal.then(value => resolve(value), reason => reject(reason))
// Can be abbreviated as follows
returnVal.then(resolve, reject)
} else {
The resolve method is called to pass the return value of the current THEN to the next THEN
resolve(returnVal)
}
}
// Test a wave
const promise = new MyPromise((resolve, reject) = > {
resolve('hello reborn jiang')})/ / common values
promise
.then((value) = > {
console.log(value, 'First then')
return 'hello world'
}).then(value= > {
console.log(value, 'Second then')})// The output is as follows
// Hello reborn jiang then
// Hello reborn jiang
/ / promise object
promise.then(value= > {
console.log(value, 'First then')
return new MyPromise((resolve, reject) = > {
resolve('success to secondThen')
})
}).then(value= > {
console.log(value, 'Second then')})// Enter the following:
// Hello reborn jiang then
// Success to secondThen
// Testing the first THEN returns a promise with a failed state
promise.then(value= > {
console.log(value, 'First then')
return new MyPromise((resolve, reject) = > {
reject('faild to secondThen')
})
}).then(value= > {
console.log(value, 'The second then succeeds')},reason= > {
console.log(reason, 'The second then fails')})// Print the following result:
// Hello reborn jiang then
// faild to secondThen
Copy the code
Congratulations, you have implemented most of the then methods, there are still a few minor details to perfect, continue to work
The rest of the content will be completed today, please stay tuned
3. The chain call to the then method recognizes that the Promise object returns itself
What happens when a chained call to the then method returns a promise as a callback to the current THEN method? Read on
The Promise code looks like this (sample)
// Promise then returns the current object
const promise = new Promise((resolve, reject) = > {
resolve('hello morning')})const promise1 = promise.then(res= > {
return promise1
})
// Print the following:
// TypeError: Chaining cycle detected for promise #<Promise>
// A type error was thrown. A Promise chain loop was detected
Copy the code
What’s the problem with using our previous code to demonstrate a chain call to the then method when the Promise object returns itself?
MyPromise code is as follows (example)
There are two ways to get the promise object returned by the current THEN in the THEN callback: the first way
const promise = new MyPromise((resolve, reject) = > {
resolve('hello reborn')})// 1. We can't get a Promise from the then method as we can from the then method, because we haven't done anything yet.
// const promise1 = promise.then(value => {
// return promise1
// })
// 2. The timer is used to return the promise1 object of the THEN method when the callback function is executed. Then the timer is used to return the promise1 object of the then method when the callback function is executed
const promise1 = promise.then(value= > {
setTimeout(() = > {
return promise1
}, 0);
}).then(res= > {
console.log(res, 'promise1') // undefined return value of promise1
})
// 3. The second then method does not return the promise returned by the previous THEN.
Copy the code
Analysis of the The second way
then(sucessCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) = > {
if (this.status === FULFILLED) {
// 1. Use timer delay to execute callback function. The principle is the same as the first method
setTimeout(() = > {
const sucessCallBackValue = sucessCallback(this.value)
resolvePromise(sucessCallBackValue, resolve, reject)
}, 0);
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
console.log('Was it executed twice?')
sucessCallback instanceof Function &&
this.sucessCallbackList.push(sucessCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
const promise = new MyPromise((resolve, reject) = > {
resolve('hello reborn')})const promise1 = promise.then(value= > {
return promise1
}).then(res= > {
console.log(res, 'promise1')})// Print the result
// Is it executed twice
// MyPromise {
// status: 'pending',
// value: null,
// reason: null,
// sucessCallbackList: [],
// faildCallbackList: [],
// resolve: [Function: resolve],
// reject: [Function: reject]
// } returnVal
// Is it executed twice
Copy the code
Analysis of the code execution results is as follows: conclusion
If you do not deal with the fact that the promise returned by the current THEN method is the return value of the callback to the current THEN method, you can result in a situation where the call chain is “faulted,” where the callback to the first state-determined PROMISE then method can be executed. The other THEN methods return promise objects that are in a pending state, The callbacks are then pushed to successCallBackList or faildCallBackList until the resolve or Reject method in the current Promise (the first THEN method) executes before the next THEN method executes its callback . The problem is that the first THEN callback pushes the resolve and reject methods that were used to call the second THEN callback into successCallBackList or faildCallBackList, causing callbacks from subsequent THEN methods to fail. The first THEN callback does not call resolve or reject, and the third then callback does not call…….. All subsequent THEN callbacks cannot be executed.
It’s easier to understand by looking at the picture below
Avoiding the self-invocation problem is simple, look at the code below
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()(value)
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) = > {
if (this.status === FULFILLED) {
setTimeout(() = > {
const successCallBackValue = successCallback(this.value)
// 1. Pass the promise2 returned by the THEN method into the resolvePromise method to judge the value returned by the then callback.
resolvePromise(promise2, successCallBackValue, resolve, reject)
}, 0);
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
// 2. Throw an exception after equality and prevent the code from executing down
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) = > {
resolve('hello reborn')})const promise1 = promise.then(res= > {
console.log(res)
return promise1
})
promise1.then(() = > {}, reason= > {
console.log(reason.message)
})
// Print the following result:
// hello reborn
// Chaining cycle detected for promise #<Promise>
Copy the code
4. Then method captures exception handling and improves other functions
Any library or framework will handle exceptions to keep the code “robust. Now improve MyPromise” analysis:
- The first thing we need to do is to make sure that our code does not report errors, so the only source of errors can be external input errors, and only external callback functions participate in our code execution
- Executor executes functions
- The callback function call to the then method
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// 1. Use try catch to catch code errors
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()(value)
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()(reason)
}
then(successCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) = > {
if (this.status === FULFILLED) {
setTimeout(() = > {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else if (this.status === REJECTED) {
const faildCallbackReason = faildCallback(this.reason)
} else {
successCallback instanceof Function &&
this.successCallbackList.push(successCallback)
faildCallback instanceof Function &&
this.faildCallbackList.push(faildCallback)
}
})
return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) = > {
resolve('hello reborn')
// 2. Print an undefined a
// console.log(a)
})
// 3
promise.then(res= >{},reason= > {
/ / executor error
console.log(reason.message, '打印错误')})// Print the result
// a is not defined type error
// Callback error
promise.then(res= > {
// 4. Create a callback error in the then method, the next THEN failed callback function needs to catch
// throw new Error("")
console.log(b)
}).then(res= >{},reason= > {
console.log(reason.message, 'Catch errors in the THEN method')})// Print the result
// b is not defined catching errors in the then method
Copy the code
At this time, only the FULFILLED state is FULFILLED in the then method, and the REJECTED and PENDING codes are not processed. Please see the processing of the REJECTED and PENDING codes in the following
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()()
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()()
}
then(successCallback, faildCallback) {
const promise2 = new MyPromise((resolve, reject) = > {
if (this.status === FULFILLED) {
setTimeout(() = > {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else if (this.status === REJECTED) {
// 1. Failed states are treated the same as successful states
// - Do exception handling for failed callback functions
// - Use the normal return value of the failed function as the return value of the successful function of the next THEN method
If a promise object is returned, the then method of the current Promise object is called. Resolve or reject is passed to the next THEN callback
// - The promise object needs to throw an exception when invoked
setTimeout(() = > {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else {
// 2. Handle pending code
// - Call a callback to the then method. Then we need to wrap the callback around a try catch
// - this is the same as the pity && REJECTED state
successCallback instanceof Function &&
this.successCallbackList.push(() = > {
Thne (thne) returns a promise (thne) after thNE (thNE) returns a promise (THNE
setTimeout(() = > {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
})
faildCallback instanceof Function &&
this.faildCallbackList.push(() = > {
setTimeout(() = > {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
reject(err)
}
}, 0); })}})return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) = > {
reject('no reborn')})// 3. Test the then method for handling failed functions
promise.then(res= >{},reason= > {
console.log(reason)
return 'Pass to the next then'
}).then(res= > {
console.log(res)
})
// Print the result:
// no reborn
// pass to the next then
// 4. Test pending state processing
promise.then(res= > {
console.log(res)
return 'hello Boom'
}).then(res= > {
console.log(res)
})
// Print the result
// hello reborn
// hello Boom
Copy the code
5. Set then to optional
The THEN method does not pass a callback as an argument, passing the state of a promise down until the THEN method accepts the passed argument when it has a callback
Promise to demonstrate
const promise = new Promise((resolve, reject) = > {
resolve('hhhhh')
})
promise.then().then().then().then(res= > {
console.log(res)
})
// Print the result
// hhhhh
Copy the code
Analysis:
- If the then method does not have a callback function, we add a callback function value => value to pass the state.
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
status = PENDING
value = null
reason = null
successCallbackList = []
faildCallbackList = []
resolve = (value) = > {
if (this.status ! == PENDING)return
this.status = FULFILLED
this.value = value
while (this.successCallbackList.length)
this.successCallbackList.shift()()
}
reject = (reason) = > {
if (this.status ! == PENDING)return
this.status = REJECTED
this.reason = reason
while (this.faildCallbackList.length) this.faildCallbackList.shift()()
}
then(successCallback, faildCallback) {
// 1. value => value The value of the previous PROMISE object can be given to the promise object returned by the current THEN, and the next THEN can be accepted
successCallback = successCallback instanceof Function ? successCallback : value= > value
// 2. Use reason => {throw reason} to catch a failed state promise, and then get the next STATE promise
faildCallback = faildCallback instanceof Function ? faildCallback : reason= > {throw reason}
const promise2 = new MyPromise((resolve, reject) = > {
if (this.status === FULFILLED) {
setTimeout(() = > {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
} else if (this.status === REJECTED) {
setTimeout(() = > {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
// 3. Let the present then be REJECTED
reject(err)
}
}, 0);
} else {
this.successCallbackList.push(() = > {
setTimeout(() = > {
try {
const successCallBackValue = successCallback(this.value)
resolvePromise(promise2, successCallBackValue, resolve, reject)
} catch (err) {
reject(err)
}
}, 0);
})
this.faildCallbackList.push(() = > {
setTimeout(() = > {
try {
const faildCallbackReason = faildCallback(this.reason)
resolvePromise(promise2, faildCallbackReason, resolve, reject)
} catch (err) {
reject(err)
}
}, 0); })}})return promise2
}
}
function resolvePromise(selfPromise, returnVal, resolve, reject) {
if (returnVal === selfPromise) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (returnVal instanceof MyPromise) {
returnVal.then(resolve, reject)
} else {
resolve(returnVal)
}
}
const promise = new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('hello reborn')},0);
})
promise.then().then().then(res= > {
console.log(res)
})
// Print the result
// hello reborn
const promise = new MyPromise((resolve, reject) = > {
setTimeout(() = > {
reject('no hello reborn')},0);
})
promise.then().then().then(res= > {
console.log(res)
}, reason= > console.log(reason))
// Print the result
// no hello reborn
Copy the code
Three, the Promise object static method implementation
1. Promise.all
Promise.all is used to solve the problem of asynchronous concurrency, and we’ll examine its features in the following sections
- Promise. all is the static method of a promise, defined by the static keyword
- A promise takes an array of parameters, either normal values or promise objects
- The promise.all method returns a Promise object, which succeeds or fails depending on the results of all the values in the array. Success status is returned if all values are successful, and failure status is returned if any values are failed. If the promise object is in a successful state, you can get the result of processing the values in the array, in the order of the array.
- If the values in the array are normal, they’re pushed directly into the result set, or if it’s a Promise object they take the values of their Promise object and push them into the result set.
// Show only the key code
// According to our analysis of the appeal we can get the following code
// 1. Define static methods
static all(array) {
// 3. Define an array to hold the result of processing the values in the array
let results = []
// return a Promise object
return new MyPromise((resolve, reject) = > {
// 4. We need to process each value with the for loop
for(let i = 0; i < array.length; i++) {
// 5. Array values can be normal data or promise objects.
if (array[i] instanceof MyPromise) {
// 7. If it is a Promise object, we need to use the then method to get the result
array[i].then(value= > results.push(value), reason= > reject(reason))
} else {
// 6. The parameters passed in are not promise objects pushed directly into the array
results.push(array[i])
}
}
// 8. The state of the Promise object needs to be changed after the for loop ends
resolve(results)
})
}
// Test a wave
function p1() {
return new MyPromise((resolve, reject) = > {
resolve('hello')})}function p2() {
return new MyPromise((resolve, reject) = > {
setTimeout(() = > {
resolve('reborn jiang')},0)
})
}
MyPromise.all([p1(), p2(), 1.2.3]).then(value= > {
console.log(value, 'promise.all return result ')})// Print the result
// [1, 2, 3, 'hello'] promise.all Returns the result
Copy the code
At this point you should notice that there is something wrong with the result returned
- The all method returns promise objects that get result sets in the wrong order.
- Five parameters were passed to the all method, and the result set was indeed missing one result from the asynchronous promise object P2.
Think about why this happens first. I haven’t figured out the second point yet, so let me update the article after thinking about it.
2. Promise.resolve
The Promise. Resolve method is designed to quickly create a Promise object analysis
- The promise. resolve method is static
- He accepts a parameter, which can be a normal value or a Promise object.
- If the acceptance is a normal value, it returns a Promise object as an argument to the Promise’s resolve method
- If it’s a Promise object, return that promise object
// Show only the key code
// Based on the above analysis we can get the following code
static resolve(value) {
// 1. If it is a PROMISE object, return a Promise object
if (value instanceof MyPromise) return value
// 2. We need to create a promise object with its value as resolve
return new MyPromise(resolve= > resolve(value))
}
// Test a wave
function p1() {
return new MyPromise((resolve, reject) = > {
resolve('hello')})}// 3. Pass in normal values
MyPromise.resolve('rrrrbbbb').then(val= > {
console.log(val)
})
// 4. Pass in the Promise object
MyPromise.resolve(p1()).then(value= > {
console.log(value)
})
// Print the following result:
// rrrrbbbb
// hello
Copy the code
3. Promise.reject
As with the promise.resolve method, promise.reject quickly creates a failed promise object analysis
- A static method
- Deliver promise object will throw an exception UnhandledPromiseRejectionWarning: hello (resolve for the promise, reject to accept the value)
- A normal value returns a Promise object and takes the parameter as a reject
The code is very simple, you can try ha ~
Finally method implementation
Finally provides a way for code to be executed after a Promise completes successfully or not. Analysis:
- The Promise method is a prototype method for the promise object
- The finally() method returns a Promise
- Finally () accepts a callback function as an argument, and will execute the specified callback function whether the result is fulfilled or Rejected
- If the finally callback is asynchronous code, the then method of the finally returned Promise object will not execute until after the callback has finished executing.
// Based on the above analysis we can get the following code
// 1. Prototype method
finally(callback) {
// this is a big pity. // 2. Both the fulfilled and rejected callback functions will be implemented. Only the then method knows the state of the Promise object
// 3. Return a PROMISE object. Then method returns a Promise object
return this.then(value= > {
callback()
return value
}, reason= > {
callback()
throw reason
})
}
const promise = new MyPromise((resolve, reject) = > {
// resolve('hello reborn')
reject('hello reborn')})// 4. Callback is used to synchronize code when output matches the target
promise.finally(() = > {
console.log('Finally executed')
}).then(res= > {
console.log(res)
}, reason= > console.log(reason))
// Print the result
// finally executed
// hello reborn
// 5. The result of the test is not as expected.
promise.finally(() = > {
setTimeout(() = > {
console.log('Finally executed')},3000)
}).then(res= > {
console.log(res)
}, reason= > console.log(reason))
// Print the result
// hello reborn
// finally executed
Copy the code
All we need to do is call resolve or Reject after the callback completes. You can use the promise.resolve method
// Based on the above analysis we can get the following code
// 1. Prototype method
finally(callback) {
// this is a big pity. // 2. Both the fulfilled and rejected callback functions will be implemented. Only the then method knows the state of the Promise object
// 3. Return a PROMISE object. Then method returns a Promise object
return this.then(value= > {
Resolve then call romise then call romise then call romise then call Romise then call Romise then call Romise then
// The outermost then method returns a Promise object, and then fetches the result of the promise generated by the resolve method as an argument to its own promise object, resolve
return MyPromise.resolve(callback()).then(() = > value)
}, reason= > {
// 5. The resolve method calls then and returns a failed Promise object. The outer THEN returns the promise returned by the call
The then method takes the value and calls reject() to pass it.
return MyPromise.resolve(callback()).then(() = > {throw reason})
})
}
Copy the code
Realization of catch method
The catch method is simply a call to the then method within the catch method, but the then method does not pass the first argument, only the failed callback argument. Look at the implementation of the code below
// Show only the key code
catch(faildCallback) {
return this.then(undefined, faildCallback)
}
promise.then(res= > { }).catch(res= > {
console.log(res)
})
// Print the result
// hello reborn
Copy the code