Promise a prototype
Promise is really just a constructor.
It has only one argument, named after the Promise/A+ specification, and we call the Promise constructor argument executor, which is the argument of the function type.
This function “automatically” takes resolve and reject as arguments.
function Promise(executor) {}Copy the code
The Promise constructor returns an instance of a Promise object that has a then method.
In the then method, the caller can define two parameters, onfulfilled and onRejected, which are function type parameters.
Wherein, onfulfilled can obtain the value of the Promise object after resolve through the parameter, and onRejected can obtain the value of the Promise object after reject.
With this value, we handle the logic after the asynchronous operation has completed.
These are the basics of the Promise/A+ specification, so let’s move on to implementing promises.
- Promise constructor
- Add to the constructor
then
Prototype method
function Promise(executor) {}Promise.prototype.then = function (onfulfilled, onrejected) {}Copy the code
Here is an example:
let promise1 = new Promise((resolve, reject) = > {
resolve('data')
})
promise1.then(data= > {
console.log(data)
})
let promise2 = new Promise((resolve, reject) = > {
reject('error')
})
promise2.then(data= > {
console.log(data)
}, error= > {
console.log(error)
})
Copy the code
When the Promise constructor is called using the new keyword, the executor argument resolve is called at an appropriate time (usually at the end of an asynchronous operation) and the processed value is executed as the resolve function argument. This value can then be obtained in ondepressing, the first function parameter of the subsequent THEN method.
Similarly, when an error occurs, the executor argument reject is called and the error message is executed as a reject parameter, which is available in the onRejected, the second function argument to the subsequent THEN method.
Therefore, when we implement a Promise, we should have two variables that store a resolve value and a Reject value
You also need a variable to store the state, which is the state of the Promise instance (pending, depressing, Rejected).
Finally, you need to provide the resolve and reject methods, which need to be provided to the developer as executor arguments
As follows:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
const self = this
this.status = PENDING
this.value = null
this.reason = null
function resolve(value) {
self.value = value
}
function reject(reason) {
self.reason = reason
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled(this.value)
onrejected(this.reason)
}
Copy the code
In order to ensure the robustness of ondepressing and onRejected, we set the default value for them. The default value is a Function. Prototype.
Because the final call to resolve is made directly by the developer in an indeterminate (usually global) environment, we either need to save this in order to retrieve the Promise instance value in resolve, or we can use the arrow function to ensure that this is executed correctly
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
const resolve = value= > {
this.value = value
}
const reject = reason= > {
this.reason = reason
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled(this.value)
onrejected(this.reason)
}
Copy the code
Why is then on the prototype of the Promise constructor, rather than inside it?
The logic of each Promise instance’s THEN method is consistent, and the instance can call the method from a stereotype (promise.propType) instead of creating a new THEN method for each instantiation to save memory
Promise improvement state
Determine the output of the following code:
let promise = new Promise((resolve, reject) = > {
resolve('data')
reject('error')
})
promise.then(data= > {
console.log(data)
}, error= > {
console.log(error)
})
Copy the code
Normally, the above code would only print data
We know that the state of a Promise instance can only change from Pending to fulfilled or from Pending to Rejected
Once a state change has been made, it cannot be changed or reversed again
The above code implementation obviously cannot meet this feature, and the last code output data and error, so the state needs to be judged and improved
As follows:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
const resolve = value= > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
}
}
const reject = reason= > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
}
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
if(this.status === FULFILLED)
onfulfilled(this.value)
if(this.status === REJECTED)
onrejected(this.reason)
}
Copy the code
Judgments are added to the resolve and Reject methods, allowing only the state of a Promise instance to change from Pending to fulfilled or from pending to Rejected
The onfulfilled and onRejected parameters of promise.proptype. then are judged. When the argument is not a function type, the default function value needs to be assigned
The above sample code should then execute smoothly
However, promises were meant to address asynchrony, and our code was all executed synchronously, missing more important logic.
Promise asynchronous implementation
Let’s take a step-by-step look at the sample code below
let promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('data')},1000);
})
promise.then(data= > {
console.log(data)
})
Copy the code
Normally, the above code should output data after 1s, but now the code does not output any information. Why is that?
The reason is simple, because our implementation logic is all synchronized. When the constructor of a Promise is instantiated, resolve is called in the setTimeout logic, meaning that the resolve method is called 1s later, changing the state of the Promise instance. However, the ondepressing in the then method is performed synchronously, and this. Status is still pending when the ondepressing is performed, and “ondepressing will be performed 1s later” is not fulfilled.
We should call the Ondepressing method at an appropriate time, which should be the time when the developer calls resolve. Then, we will first save the Ondepressing method sent in by the developer when the status is pending. Execute in the resolve method
As follows:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledFunc = Function.prototype
this.onRejectedFunc = Function.prototype
const resolve = value= > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledFunc(this.value)
}
}
const reject = reason= > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedFunc(this.reason)
}
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
if (this.status === FULFILLED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
if (this.status === PENDING) {
this.onFulfilledFunc = onfulfilled
this.onRejectedFunc = onrejected
}
}
Copy the code
Through testing, we found that the code we implemented can support asynchronous execution.
We know that Promise’s then method executes asynchronously. Look at another π°
let promise = new Promise((resolve, reject) = > {
resolve('data')
})
promise.then(data= > {
console.log(data)
})
console.log(1)
Copy the code
Normally, the above example would print 1 followed by data in that order
The code we implemented doesn’t take this into account and actually prints data first and then 1. Therefore, the resolve and Reject executions need to be placed on the task queue. Let’s just put them in setTimeout to ensure that they execute asynchronously. (This is not strictly done, as many Promise implementation libraries use MutationObserver to mimic nextTick to ensure that promises are microtasks.)
const resolve = value= > {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() = > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledFunc(this.value)
}
}, 0);
}
const reject = reason= > {
setTimeout(() = > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedFunc(this.reason)
}
}, 0);
}
executor(resolve, reject)
Copy the code
This way, when executor(Resolve, Reject) is executed, the Promise task will be executed only in nextTick and will not block the synchronization task
At the same time, we added a statement to the resovle method to determine whether the value is a Promise instance
Here is the complete code so far:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledFunc = Function.prototype
this.onRejectedFunc = Function.prototype
const resolve = value= > {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() = > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledFunc(this.value)
}
}, 0);
}
const reject = reason= > {
setTimeout(() = > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedFunc(this.reason)
}
}, 0);
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
if (this.status === FULFILLED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
if (this.status === PENDING) {
this.onFulfilledFunc = onfulfilled
this.onRejectedFunc = onrejected
}
}
Copy the code
Promise complete details
Add multiple THEN methods before the state of the Promise instance changes
let promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('data')},1000);
})
promise.then(data= > {
console.log(` 1:${data}`)
})
promise.then(data= > {
console.log(` 2:${data}`)})Copy the code
Normally, the above code returns output
1: data
2: data
Copy the code
Our implementation only prints 2: data, because the onledFunc in the second THEN method overwrites the onledFunc in the first THEN method
To solve this problem, simply store all of the onimplemented ledFunc’s from the then methods in an array called onimplemented ray, and execute the onimplemented ledFunc’s in turn when the current Promise is decided. The same applies to onRejectedFunc
The modified code:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value= > {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() = > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledArray.forEach(func= > {
func(value)
})
}
}, 0);
}
const reject = reason= > {
setTimeout(() = > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedArray.forEach(func= > {
func(reason)
})
}
}, 0);
}
executor(resolve, reject)
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
if (this.status === FULFILLED) {
onfulfilled(this.value)
}
if (this.status === REJECTED) {
onrejected(this.reason)
}
if (this.status === PENDING) {
this.onFulfilledArray.push(onfulfilled)
this.onRejectedArray.push(onrejected)
}
}
Copy the code
Another detail that needs to be worked out is that if something goes wrong in the constructor, it will start automatically. The Promise instance is in the rejected state, so we use try… The catch block wraps the executor
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
Copy the code
Promise chain call
Let’s start with a question: Determine the output of the following code
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000);
})
promise.then(data= > {
console.log(data)
return `${data} next then`
}).then(data= > {
console.log(data)
})
Copy the code
The above code prints Andy 1s later, followed by Andy next then
We can see that the THEN method of the Promise instance supports chain call. After outputing the value processed by resolve, if the new value is explicitly returned synchronously in the ondepressing function of the THEN method body, Then you will get the new value in the Ondepressing function of the new Promise instance then method
What if another Promise instance is returned in the onfulfilled function of the first THEN method body
Look at the following code:
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000);
})
promise.then(data= > {
console.log(data)
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(`${data} next then`)},2000);
})
}).then(data= > {
console.log(data)
})
Copy the code
This code will print Andy after 1s and then Andy next then after 2s
Therefore, the onfulfilled function and onRejected function of a Promise instance then method support returning a Promise instance again, and also support returning the normal value of a non-Promise instance
Moreover, the normal value of the returned Promise instance or non-Promise instance will be passed to the onfulfilled function or onRejected function of the next THEN method
Preliminary implementation of chain call
In order to support chain calls of the THEN method, each OF the ONfulfilled and onRejected functions of the THEN method should return a Promise instance
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value= > {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() = > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledArray.forEach(func= > {
func(value)
})
}
}, 0);
}
const reject = reason= > {
setTimeout(() = > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedArray.forEach(func= > {
func(reason)
})
}
}, 0);
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
let promise2
if (this.status === FULFILLED) {
return promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
try {
let result = onfulfilled(this.value)
resolve(result)
} catch (e) {
reject(e)
}
}, 0); })}if (this.status === REJECTED) {
return promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
try {
let result = onrejected(this.reason)
resolve(result)
} catch (e) {
reject(e)
}
}, 0); })}if (this.status === PENDING) {
return promise2 = new Promise((resolve, reject) = > {
this.onFulfilledArray.push(() = > {
try {
let result = onfulfilled(this.value)
resolve(result)
} catch (e) {
reject(e)
}
})
this.onRejectedArray.push(() = > {
try {
let result = onrejected(this.reason)
resolve(result)
} catch (e) {
reject(e)
}
})
})
}
}
Copy the code
The most important thing to understand here is the logic in the this.status === PENDING decision branch, which is also the most difficult to understand. When a Promise instance calls its THEN method, it should return a Promise instance that returns the promise2 returned in the this.status === PENDING judgment branch. So when is promise2 going to be resolved? This should be done after the asynchronous processing is completed, when the functions in the ondefaulledarray or onRejectedArray array are executed in sequence
What should the functions in the ononledarray and onRejectedArray arrays do?
Switch the state of promisE2 and initiate a resolution
Perfect implementation of chain call
We continue implementing the THEN method to explicitly return a Promise instance.
As follows:
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000);
})
promise.then(data= > {
console.log(data)
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(`${data} next then`)},2000);
})
}).then(data= > {
console.log(data)
})
Copy the code
Based on the first onfulfilled function and onRejected function returning a normal value, it is not difficult to implement this onfulfilled function and onRejected function returning a Promise instance. This is a pity (this. Value) statement and let result = onfulfilled(this. Reason) statement. Change the variable result from a normal value to a Promise instance.
The variable result can be either a common value or a Promise instance, so we abstract the resulvePromise method for unified processing. Code that has been modified to an existing implementation. As follows:
const resolvePromise = (promise2, result, resolve, reject) = >{}Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
let promise2
if (this.status === FULFILLED) {
return promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0); })}if (this.status === REJECTED) {
return promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0); })}if (this.status === PENDING) {
return promise2 = new Promise((resolve, reject) = > {
this.onFulfilledArray.push(() = > {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedArray.push(() = > {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
}
Copy the code
The task now is to implement the resolvePromise function, which takes four parameters:
promise2
: returns an instance of Promiseresult
:onfulfilled
ζonrejected
The return value of the functionresolve
:promise2
ηresolve
methodsreject
:promise2
ηreject
methods
const resolvePromise = (promise2, result, resolve, reject) = > {
// Execute reject when result and promise are equal, i.e., when onfulfilled returns promise2
if (result === promise2) {
reject(new TypeError('error due to circular reference'))}// Whether you have performed onfulfilled or onRejected
let consumed = false
let thenable
if (result instanceof Promise) {
if (result.status === PENDING) {
result.then((data) = > {
resolvePromise(promise2, data, resolve, reject)
}, reject)
} else {
result.then(resolve, reject)
}
return
}
let isComplexResult = (target= > typeof target === 'function' || typeof target === 'object') && target ! = =null
// If the return is of type Promise
if (isComplexResult(result)) {
try {
thenable = result.then
// Determine if the return value is of type Promise
if (typeof thenable === 'function') {
thenable.call(result, function (data) {
if (consumed) {
return
}
consumed = true
return resolvePromise(promise2, data, resolve, reject)
}, function (error) {
if (consumed) {
return
}
consumed = true
return reject(error)
})
} else {
resolve(result)
}
} catch (e) {
if (consumed) {
return
}
consumed = true
return reject(e)
}
} else {
resolve(result)
}
}
Copy the code
The first step in the resolvePromise method is to handle an “infinite loop” and throw an error if one occurs
How do we understand this? As the Promise specification points out, this is what happens when an “infinite loop” occurs:
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000);
})
promise.then(onfulfilled = data= > {
console.log(data)
return onfulfilled(data)
}).then(data= > {
console.log(data)
})
Copy the code
The ondepressing function returns result: If result is not a Promise instance, object, or function, but an ordinary value (isCompexResult function is used to judge this), then promise2 will be resolved directly
For the result returned by the ondepressing function: If result contains the THEN method, then we call that method thenable, indicating that result is an instance of Promise. When executing the then method of that instance, The return result can also be a Promise instance type or a plain value, so resolvePromise is also recursively called.
As follows:
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000);
})
promise.then(data= > {
console.log(data)
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(`${data} next then`)},2000);
}).then(data= > {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(`${data} next then`)},2000);
})
})
}).then(data= > {
console.log(data)
})
Copy the code
The above code will output Andy after 1s and Andy next then next then
Now the full code:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledArray = []
this.onRejectedArray = []
const resolve = value= > {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
setTimeout(() = > {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledArray.forEach(func= > {
func(value)
})
}
}, 0);
}
const reject = reason= > {
setTimeout(() = > {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedArray.forEach(func= > {
func(reason)
})
}
}, 0);
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
const resolvePromise = (promise2, result, resolve, reject) = > {
// Execute reject when result and promise are equal, i.e., when onfulfilled returns promise2
if (result === promise2) {
reject(new TypeError('error due to circular reference'))}// Whether you have performed onfulfilled or onRejected
let consumed = false
let thenable
if (result instanceof Promise) {
if (result.status === PENDING) {
result.then((data) = > {
resolvePromise(promise2, data, resolve, reject)
}, reject)
} else {
result.then(resolve, reject)
}
return
}
let isComplexResult = (target= > typeof target === 'function' || typeof target === 'object') && target ! = =null
// If the return is of type Promise
if (isComplexResult(result)) {
try {
thenable = result.then
// Determine if the return value is of type Promise
if (typeof thenable === 'function') {
thenable.call(result, function (data) {
if (consumed) {
return
}
consumed = true
return resolvePromise(promise2, data, resolve, reject)
}, function (error) {
if (consumed) {
return
}
consumed = true
return reject(error)
})
} else {
resolve(result)
}
} catch (e) {
if (consumed) {
return
}
consumed = true
return reject(e)
}
} else {
resolve(result)
}
}
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
let promise2
if (this.status === FULFILLED) {
return promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0); })}if (this.status === REJECTED) {
return promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
}, 0); })}if (this.status === PENDING) {
return promise2 = new Promise((resolve, reject) = > {
this.onFulfilledArray.push(() = > {
try {
let result = onfulfilled(this.value)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
this.onRejectedArray.push(() = > {
try {
let result = onrejected(this.reason)
resolvePromise(promise2, result, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
}
Copy the code
Promise penetration
At this point, with the exception of static methods, our Promise implementation is almost 95% complete
Why not 100%? There is one detail left to complete, so let’s look at the following code to determine the output
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000);
})
promise.then(null).then(data= > {
console.log(data)
})
Copy the code
The above code will print Andy 1s later.
This is Promise penetration: passing a non-functional value as an argument to a THEN () function actually resolves to THEN (NULL), in which case the resolution result of the previous Promise object “passes” into the argument of the next THEN method
How do I implement Promise penetration
It’s actually very simple, and it’s already implemented. In the implementation of the THEN () method, we have added the following judgment to the onfulfilled and onRejected functions:
Promise.prototype.then = function (onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data= > data
onrejected = typeof onrejected === 'function' ? onrejected : error= > { throw error }
// ...
}
Copy the code
If ondepressing is not a function type, a default value is given, which is the function that returns its parameters. The same goes for the onreject function. This code implements the logic of “penetration”
Promise static methods and other methods
- Promise.proptype.catch
- Promise.resolve
- Promise.reject
- Promise.all
- Promise.race
Promise. Prototype. The catch
Promise.prototype.catch can be used to catch exceptions. Usage:
const promise = new Promise((resolve, reject) = > {
setTimeout(() = > {
reject('error')},1000);
})
promise.then(data= > {
console.log(data)
}).catch(error= > {
console.log(error)
})
Copy the code
The above code will print error after 1s
In this case, it is equivalent to the following code
Promise.prototype.catch = function (catchFunc) {
return this.then(null, catchFunc)
}
Copy the code
Because we know that the second argument to the then() method also catches exceptions, we can implement promise.prototype.catch relatively easily with this feature
Promise. Resolve to achieve
The MDN: promise.resolve (value) method returns a Promise instance object resolved with the given value
π° :
Promise.resolve('data').then(data= > {
console.log(data)
})
console.log(1)
Copy the code
The code above prints 1 and then data
Implementation:
Promise.resolve = function (value) {
return new Promise((resolve, reject) = > {
resolve(value)
})
}
Copy the code
Implement promise.reject (reason)
Promise.reject = function (reason) {
return new Promise((resolve, reject) = > {
reject(reason)
})
}
Copy the code
Promise. All implementations
MDN: Promise.all(iterable) returns an instance of Promise, This instance is’ resolved ‘for all Promise instances in the iterable argument or completes the resolve call if the Promise instance is not included in the argument; If the Promise instance in the parameter has a failure (Rejected), the instance callback fails (Reject) for the same reason that the first Promise instance failed
π° :
let promise1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000)})let promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy')},1000)})Promise.all([promise1, promise2]).then(data= > {
console.log(data)
})
Copy the code
The above code will print [” Andy “, “Andy “] after 1s.
Implementation:
Promise.all = (promiseArray) = > {
if (!Array.isArray(promiseArray)) {
throw new TypeError('The arguments should be an array! ')}return new Promise((resolve, reject) = > {
try {
let resultArray = []
const len = promiseArray.length
if (len === 0) {
return resolve(resultArray)
}
let count = 0
for (let i = 0; i < len; i++) {Promise.resolve(promiseArray[i]).then(data= > {
count++
resultArray[i] = data
if (count === len) {
resolve(resultArray)
}
}, reject)
}
} catch (e) {
reject(e)
}
})
}
Copy the code
If the parameter is not an array type, throw an error. Promise.all returns a Promise instance that will be resolved after all the Promise instances in the promiseArray have been resolved. The result is an array, This array holds resolution values for all Promise instances in the promiseArray
Promise. Race
π° :
let promise1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy1')},1000)})let promise2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('andy2')},2000)})Promise.race([promise1, promise2]).then(data= > {
console.log(data)
})
Copy the code
This code will print andy1 after 1s
Implementation:
Promise.race = (promiseArray) = > {
if (!Array.isArray(promiseArray)) {
throw new TypeError('The arguments should be an array! ')}return new Promise((resolve, reject) = > {
try {
const len = promiseArray.length
if (len === 0) {
return resolve()
}
for (let p of promiseArray) {
Promise.resolve(p).then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
Copy the code
Synchronously execute the THEN methods of all Promise instances in the promiseArray array, and the first resolve example directly triggers a new Promise instance
conclusion
Promise/A+
Promise
Once the state of is changed, it cannot be changed. (see 3.1).then
ε.catch
Will return a new onePromise
.catch
Upper-level errors can be caught no matter where it is connected. (see 3.2)- in
Promise
, returns any notpromise
Is wrapped around the value ofpromise
Object, for examplereturn 2
Will be packaged asreturn Promise.resolve(2)
γ Promise
η.then
or.catch
Can be called multiple times when ifPromise
Once the internal state changes and there is a value, then each subsequent call.then
or.catch
“Will directly get the value. (see 3.5).then
or.catch
δΈreturn
aerror
The object does not throw an error, so it is not followed.catch
Capture. (see 3.6).then
ζ.catch
The returned value cannot bepromise
Itself, otherwise it will cause an endless loop. (see 3.7).then
or.catch
The argument to is expected to be a function. Passing in a non-function will result in value penetration. (see 3.8).then
Methods can take two arguments, the first to handle the successful function, the second to handle the failed function, and at some point you can saycatch
ζ―.then
A handy way to write the second parameter. (see 3.9).finally
Method also returns onePromise
In hisPromise
At the end of the day, whatever the result isresolved
orrejected
, will execute the callback function inside..finally()
Methods no matterPromise
How will the final state of the object be executed.finally()
The callback function of the method does not take any arguments, which means that you are in.finally()
There’s no way to knowPromise
The final state of- What it ultimately returns isThe last Promise object valueException is returned if an exception is thrown
Promise
object Promise.all()
Is used to receive a set of asynchronous tasks, execute the asynchronous tasks in parallel, and wait until all the asynchronous operations are complete before executing the callback.race()
The function also receives a set of asynchronous tasks and executes them in parallel, preserving only the results of the first completed asynchronous operation. The other methods are still executed, but the results are discardedPromise.all().then()
The order sum of the arrays in the resultPromise.all()
The received arrays are in the same orderall
εrace
If there is an asynchronous task in the array that throws an exception, only the first error thrown will be caught, and will bethen
The second argument to or aftercatch
Capture; This does not affect the execution of other asynchronous tasks in the array