Cause of occurrence
Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
Before Promise came along
function foo(url, successCallback, failCallback) {
// Use timer to simulate network request
setTimeout(() = > {
if (url = 'example') {
successCallback(213)}else {
failCallback('err')}},2000)
}
foo('example'.msg= > console.log(`success --- ${msg}`),
err= > console.log(`fail --- ${err}`))Copy the code
The way we process asynchronous results is by using callback functions, but how asynchronous functions receive callback functions and how callback arguments are used is defined by the wrappers themselves, which means that different wrappers have different coding styles and encapsulation methods
For callers, if they need to use the method of wrapped asynchronous operation, they have to see how the wrapper is wrapped, that is, to see the document, to see how the API is called, which increases the communication cost between the user and the wrapper. Right
Promises/A+ : Promises/A+ : Promises/A+ : Promises/A+
In ES6, however, there is a unified, official way of encapsulating asynchronous functions, and this is the Promise
The biggest benefit of promises is that they allow asynchronous methods to return values just as synchronous methods do, but only with a promise, a pending state that must be switched to a success or failure state in the future
The basic use
- Promise is a class that can be translated as a Promise, a Promise, or an appointment
- A promise is a promise, or a credential, that will later tell the user whether the asynchronous operation was successful and return the corresponding result
Promsie takes one argument, which is a callback function called the executor function
// The executor function is executed immediately when a Promise instance is created
new Promise(() = > console.log('Executor will be executed immediately')) // => Executor will be executed immediately
Copy the code
function getResponse(url) {
// Store asynchronous functions that need to be executed in executor functions, which can also contain synchronous code
// Executor needs to pass in two arguments whose types are required to be functions
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
// When successful, execute the resolve callback
resolve('success')}else {
When it fails, the reject callback is executed
reject('fail')}},2000)})}const promise = getResponse('example')
// Promise has then methods
// Parameter 1 corresponds to the resolve parameter that will be executed when the resolved state is resolved
// Parameter 2 corresponds to executor reject in rejected === Parameter 2
promise.then(res= > console.log(res), err= > console.log(err))
Copy the code
Three states
- Pending — Pending
- The status of the executor function after it has been executed
- It is not known whether the asynchronous request will ultimately succeed or fail
- This is a pity (resolved) — Asynchronous success
- Rejeced – Asynchronous failure
Promise states are locked once they change and cannot be changed
Pending can convert resolved | rejected
But reolved and Rejected are not interchangeable
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
resolve('success')
// The code will not report an error
// But reject is not executed -- silent failure
reject('fail')}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err))
Copy the code
resolve
The parameters are either basic data types or plain objects - promise's state is Reolved
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
resolve('success')}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err)) // => success
Copy the code
The parameter type is Promise
The state of the promise is handed over, that is, the promise and why does the state end up being determined by the incoming promise
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
// The resolve state is successful
// But the argument is a new promise, so the state decision has been handed over
// This is determined by the parameter Promise
// So the result is rejected
resolve(new Promise((resolve, reject) = > reject('Failed')))}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err)) // => Failed
Copy the code
The argument is a thenable object
When a method implements the THEN method correctly, it is called a thenable object, which is essentially an object that implements the THEN method
The final state of the promise is determined by the then method
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
resolve({
// Then methods are executed automatically
then(resolve, reject) {
// The final state is determined by the then method
reject("It failed in the end.")}}}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err)) // => Failed
Copy the code
Instance methods
A promise has three instance methods: then, catch, and finally
then
// The then method takes two arguments
Resolve === 1 - This parameter is optional
// Parameter 2 corresponds to executor reject when the rejected state is rejected. Parameter === Parameter 2 - this parameter is optional
// The then method converts the return value to a new Promise, so you can make chain calls
promise.then(res= > console.log(res), err= > console.log(err))
.then(res= > console.log(res))
Copy the code
Then methods can be called multiple times, and the corresponding THEN methods are executed sequentially
const promise = getResponse('example')
promise.then(() = > console.log('success --- 1'))
promise.then(() = > console.log('success --- 2'))
promise.then(() = > console.log('success --- 3'))
/* => success --- 1 success --- 2 success --- 3 */
Copy the code
The return value of then
- The return value is plain (basic data type + plain object)
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
resolve('success')}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > 123).then(res= > console.log(res)) / / = > 123
Then (res => new promise ((resolve, reject) => resolve(123))) */
Copy the code
The default return value for a function that does not return any value is undefined, so a then method that does not return any value returns a new promise instance, and a resolve(undefined) method that does not return any value.
- The return value is an instance of Promise
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
resolve('success')}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > new Promise((resolve, reject) = > resolve('22222')))
.then(res= > console.log(res)) / / = > 22222
Then (res => new promise ((resolve, reject)) => {resolve(new promise ((resolve, reject)) reject) => resolve('22222'))) })).then(res => console.log(res)) */
Copy the code
- The return value is a thenable object
function getResponse(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url === 'example') {
resolve('success')}else {
reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > ({
then(resolved, rejected) {
resolved(111111)
}
})).then(res= > console.log(res)) / / = > 111111
Then (res => new promise (resolve, reject) => {resolve({resolve, rejected) { resolved(111111) } }) })).then(res => console.log(res)) */
Copy the code
catch
When we only want to capture the reject state, we typically set the first parameter of the THEN method to undefined
promise.then(undefined.() = > console.log('error'))
Copy the code
For ES6, the corresponding syntactic sugar form code is provided
promise.catch(() = > console.log('error'))
Copy the code
When a promise executes reject or throws an exception, the promise goes into the Rejected state, so the catch method is fired
function getResponse(url) {
return new Promise((resolve, reject) = > {
// When promise throws an exception -- the state switches to Rejected
// The corresponding error object is passed as a reject parameter
throw new Error('error')})}const promise = getResponse()
promise.catch(err= > {
console.log('err', err)
})
Copy the code
If there are more than one catch method, the catch methods are executed sequentially
function getResponse(url) {
return new Promise((resolve, reject) = > {
reject('error')})}const promise = getResponse()
promise.catch(() = > console.log('err1'))
promise.catch(() = > console.log('err2'))
promise.catch(() = > console.log('err3'))
Copy the code
The return value of a PROMISE is still a promise
promise.then(() = > console.log('success'),() = > console.log('error'))
Copy the code
Can be converted to
promise.then(() = > console.log('success'))
.catch(() = > console.log('error'))
Copy the code
Note:
function getResponse(url) {
return new Promise((resolve, reject) = > {
reject('error')})}const promise = getResponse()
// The next two lines are actually two calls to promise
// So for line 11, it implements only resolve, not reject
// So an error is reported when the promise state is Rejected
promise.then(() = > console.log('success'))
promise.catch(() = > console.log('error'))
Copy the code
If a catch corresponds to multiple promises, the catch catches the first reject promise and terminates the execution of all promises
function getResponse(url) {
return new Promise((resolve, reject) = > {
resolve('success')})}const promise = getResponse()
The catch method is raised when a reject promise is returned by the then method
promise
.then(() = > new Promise((resolve, reject) = > reject('Wrong')))
.catch(() = > console.log('reject')) // => reject
Copy the code
function getResponse(url) {
return new Promise((resolve, reject) = > {
reject('error')})}const promise = getResponse()
The catch method is raised when the getResponse method returns a reject promise
promise
.then(() = > new Promise((resolve, reject) = > reject('Wrong')))
.catch(() = > console.log('reject')) // => reject
Copy the code
The return value of the catch method
The return value of the catch method is also a Promise instance, so the rule for the return value is the same as the rule for the return value of the resolve method
function getResponse(url) {
return new Promise((resolve, reject) = > {
reject('error')})}const promise = getResponse()
promise
.catch(() = > {
console.log('reject')
return 'Return value of catch method'
})
// Because the return value of the catch method is also converted to a new Promise instance
// So in this case, the then method after the catch method is also executed normally
.then(res= > console.log(res)) // => The return value of the catch method
Copy the code
finally
Finally is a new feature in ES9(ES2018) : code that indicates that a Promise object will eventually be executed whether it becomes a pity or a Reject state
The finally method does not accept parameters because it executes whether the preceding state is a pity or reject state
function getResponse(url) {
return new Promise((resolve, reject) = > {
reject('error')})}const promise = getResponse()
promise
.then(res= > console.log(res))
.catch(err= > console.log(err))
.finally(() = > console.log('Code that must be executed'))
Copy the code
Class method
resolve
The use of promise.resolve is equivalent to new Promise, and the resolve operation is performed
const promise = Promise.resolve({ name: 'Klaus' })
promise.then(res= > console.log(res))
Copy the code
Is equivalent to
const promise = new Promise((resolve, reject) = > resolve({ name: 'Klaus' }))
promise.then(res= > console.log(res))
Copy the code
The promise. resolve method can accept arguments of the type
- A parameter is a common value or object
- The argument itself is a Promise
- The argument is a thenable
The specific judgment rules are the same as before
reject
Promise. Reject is used the same way as New Promise, except that the reject method is called
Reject is passed directly to the catch as a reject state parameter, regardless of its form
const promise = Promise.reject('reject')
promise.catch(res= > console.log(res))
Copy the code
Is equivalent to
const promise = new Promise((resolve, reject) = > reject('error'))
promise.catch(res= > console.log(res))
Copy the code
all
The idea is to wrap multiple promises together to form a new Promise
The new Promise status is determined jointly by all the promises in the package
- When all the Promise states become the fulfilled state, the new Promise state becomes the fulfilled state, and all the Promise return values will be formed into an array
- When a Promise is reject, the new Promise is REJECT and takes the return value of the first REJECT as an argument
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > resolve('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
The return value of the promise. all method is still a Promise instance
Promise.all([p1, p2, p3])
.then(res= > console.log(res))
.catch(err= > console.log(err))
// => ['11111', '22222', '33333']
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > resolve('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
// The order of the output results is determined by the order of the parameters, rather than by the promise which first transitions to a depressing state
// Even if P1 is determined as the state of depressing before P2, the result of P1 will still be output as the second value of the array
Promise.all([p2, p1, p3])
.then(res= > console.log(res))
.catch(err= > console.log(err))
// => ['22222', '11111', '33333']
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
// When a promise status is set to Rejected, a catch is entered
Promise.all([p2, p1, p3])
.then(res= > console.log(res))
.catch(err= > console.log(err))
/ / = > 22222
Copy the code
allsettled
The method will come to a final state only when all the Promise is settled, whether this is a pity or a reject
The return value of the AllSettled method is also a promise, and the result of this promise must be a pity
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
Promise.allSettled([p1, p2, p3])
.then(res= > console.log(res))
.catch(err= > console.log(err))
/* => [ { status: 'fulfilled', value: '11111' }, { status: 'rejected', reason: '22222' }, { status: 'fulfilled', value: '33333'}] * /
Copy the code
race
The arguments to the RACE method accept an array of promises and return a promise
The state of the returned promise is determined by the promise whose state is determined by the first one
If the state of the first stateful promise is Resolved, the returned promise is Resolved
If the first stateful promise is in the rejected state, then the returned promise is in the Rejected state
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
Promise.race([p1, p2, p3])
.then(res= > console.log('res', res))
.catch(err= > console.log('err', err))
/* => res 11111 */
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
Promise.race([p1, p2, p3])
.then(res= > console.log('res', res))
.catch(err= > console.log('err', err))
/* => err 22222 */
Copy the code
any
The any method is new in ES12 and is similar to the Race method
The any method will wait for a depressing state before deciding the state of the new Promise
If all promises are reject, then you wait until all promises are rejected
If all promises are reject, an AggregateError is reported
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))
Promise.any([p1, p2, p3])
.then(res= > console.log('res', res))
.catch(err= > console.log('err', err))
/* => res: 11111 */
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > reject('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > reject('33333'), 3000))
Promise.any([p1, p2, p3])
.then(res= > console.log('res', res))
.catch(err= > console.log('err', err))
/* => err: AggregateError: All promises were rejected returns a AggregateError instance is equal to the Promise. Any internal reject method performs for reject (new AggregateError (' All promises were rejected')) */
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > reject('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > reject('33333'), 3000))
// For AggregateError instances, we can use the errors method to get all the arguments that promise passes in the reject method
// The type is array
Promise.any([p1, p2, p3])
.then(res= > console.log('res', res))
.catch(err= > console.log(err.errors))
// => ['11111', '22222', '33333']
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > reject('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > reject('33333'), 3000))
// Of course, the output order of error messages still corresponds to the order of arguments passed in
// Instead of corresponding to the transition order of the promise state
Promise.any([p2, p1, p3])
.then(res= > console.log('res', res))
.catch(err= > console.log(err.errors))
// => ['22222', '11111', '33333']
Copy the code