Basic Concepts of Promise
ES6 complies with the Promise A+ specification
Promise has three states:
- Waiting for,
pending
) - Cash (
fulfilled
) also called solutionresolved
- Refuse to (
reject
)
The state is from pending to depressing or reject. Once the state is set, it is irreversible
Promise’s then method takes two arguments
- The callback function that onResolved
- OnRejected rejects the callback function
The then method returns a promise and can be called by a promise multiple times
Promise static method
- Promise. Resolve (reference 1)
- Promise. Reject (reference 1)
Promise. Resolve (reference 1)
Parameter 1: resolve the value of this contract; Passing extra parameters will be ignored
This static method behaves like an empty wrapper if the argument is a contract ———— js elevation system will resolve the value for you
let p = Promise.resolve(7);
setTimeout(() = > {
console.log(p === Promise.resolve(p), p)
}, 0);
setTimeout(() = > {
console.log(p === Promise.resolve(Promise.resolve(p)), p)
}, 0);
Copy the code
See no
This method is idempotent and preserves the state of the input contract
let p = new Promise(() = >{});
setTimeout(() = > {
console.log(p === Promise.resolve(p), p)
}, 0);
setTimeout(() = > {
console.log(p === Promise.resolve(Promise.resolve(p)), p)
}, 0);
Copy the code
Note: This static method can wrap any non-term value, including an error object, and convert it to a term. So it can lead to behavior that doesn’t meet expectations
let p = Promise.resolve(new Error('err'));
setTimeout(() = > {
console.log(p)
}, 0);
Copy the code
Promise. Reject (reference 1)
Instantiate a reject contract and throw an asynchronous error (this error cannot be caught by a try/catch, only by a reject handler)Copy the code
Parameter 1: the reason for the rejection period. This parameter is also passed to subsequent rejection handlers
let p = Promise.reject(3);
setTimeout(() = > {
console.log(p)
}, 0);
p.then(null.e= > {
setTimeout(() = > {
console.log(p)
}, 0);
})
Copy the code
Note that promise.reject () does not copy the idempotent logic of promise.resolve (); if it is passed a date, that date becomes the rejection handler it returns
console.log(Promise.reject(Promise.reject()))
Copy the code
Try /catch can catch only synchronous errors. Asynchronous errors, such as rejected contracts, cannot be caught
try{
throw new Error('err')}catch (e) {
console.log(e)
}
try{
Promise.reject(new Error('p-err'))}catch (e) {
console.log(e)
}
Copy the code
Instance method of Promise
then(onResolved, onRejected)
- Arguments are two functions and can be accepted by the function
Promise
bypending
Parameters passed to the determined state onResolved
The cashing processonRejected
Rejection handler
function onResolved(id, data) {
setTimeout(() = > {
console.log("resolved", id, data)
}, 0);
}
function onRejected(id, data) {
setTimeout(() = > {
console.log("rejected", id, data)
}, 0);
}
let p1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('resolve')},3000);
})
let p2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
reject('reject')},3000);
})
p1.then((data) = > {
onResolved('p1', data)
}, (data) = > {
onRejected('p1', data)
})
p2.then((data) = > {
onResolved('p2', data)
}, (data) = > {
onRejected('p2', data)
})
Copy the code
After 3 s
The two processing arguments in then() are optional, and any non-functional arguments passed to then() are ignored. If parameter 1 does not want to pass, but parameter 2 needs to be passed, simply pass NULL at parameter 1
p1.then('dfsdfdssff') // Non-function handlers are ignored and not recommended
p2.then(null.() = > onRejected('p2')) // Do not pass the onResolved code
Copy the code
setTimeout(() = > {
console.log(p1)
}, 0);
setTimeout(() = > {
console.log(p2)
}, 0);
setTimeout(() = > {
console.log(p1 === p2)
}, 0);
Copy the code
This new instance of P2 is built based on the onResolved handler return value. That is, the return value of the handler generates a new term using a promise.resolve () wrapper. If no handler is provided, promise.resolve () wraps the value of the previous term. If no return statement is displayed (that is, return XXX), promise.resolve () wraps the default return value undefined
Return value for then() – onResolved
let p1 = Promise.resolve('foo');
// then does not pass the handler
let p2 = p1.then();
setTimeout(() = > {
console.log(p2)
}, 0);
Copy the code
If no return statement is displayed (that is, return XXX), promise.resolve () wraps the default return value undefined
let p3 = p1.then(() = > {
return undefined
})
let p4 = p1.then(() = > {})
let p5 = p1.then(() = > Promise.resolve())
setTimeout(() = > {
console.log(p3) //Promise {<fulfilled>: undefined}
console.log(p4) //Promise {<fulfilled>: undefined}
console.log(p5) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code
If there is a displayed return value, promise.resolve () wraps that value
let p6 = p1.then(() = > {
return 'bar'
})
let p7 = p1.then(() = > Promise.resolve('bar'))
setTimeout(() = > {
console.log(p6) //Promise {<fulfilled>: "bar"}
console.log(p7) //Promise {<fulfilled>: "bar"}
}, 0);
Copy the code
Promise.resolve() preserves the returned term
let p8 = p1.then(() = > {
return new Promise(() = >{})})let p9 = p1.then(() = > {
return Promise.resolve()
})
setTimeout(() = > {
console.log(p8) //Promise {<pending>}
console.log(p9) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code
Throwing an exception returns the rejected contract
let p10 = p1.then(() = > {
throw 'baz'
})
setTimeout(() = > {
console.log(p10)
}, 0);
Copy the code
If an error value is returned, the rejection action as above is not triggered, but the error object is wrapped in a resolution contract
let p11 = p1.then(() = > {
return Error('qux')})setTimeout(() = > {
console.log (p11),,,Promise {<fulfilled>: undefined}},0);
Copy the code
The return value of then() – onRejected
The onRejected handler is also wrapped in promise.resolved (). The onRejected handler’s job is to catch asynchronous errors, so it returns a resolution date if it does not throw an exception after catching an error
let p1 = Promise.reject('foo');
// then does not pass the handler
let p2 = p1.then();
setTimeout(() = > {
console.log(p2)
}, 0);
Copy the code
When an error is caught, instead of throwing an exception, the Promise. Resolved () wrapped date is returned
let p3 = p1.then(null.() = > {
return undefined
})
let p4 = p1.then(null.() = > {})
let p5 = p1.then(null.() = > Promise.resolve())
setTimeout(() = > {
console.log(p3) //Promise {<fulfilled>: undefined}
console.log(p4) //Promise {<fulfilled>: undefined}
console.log(p5) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code
If no return statement is displayed (that is, return XXX), promise.resolve () wraps the default return value undefined
let p6 = p1.then(null.() = > {
return 'bar'
})
let p7 = p1.then(null.() = > Promise.resolve('bar'))
setTimeout(() = > {
console.log(p6) //Promise {<fulfilled>: "bar"}
console.log(p7) //Promise {<fulfilled>: "bar"}
}, 0);
Copy the code
If there is a displayed return value, promise.resolve () wraps that value
let p8 = p1.then(null.() = > {
return new Promise(() = >{})})let p9 = p1.then(null.() = > {
return Promise.resolve()
})
setTimeout(() = > {
console.log(p8) //Promise {<pending>}
console.log(p9) //Promise {<fulfilled>: undefined}
}, 0);
Copy the code
Throwing an exception returns the rejected contract
let p10 = p1.then(null.() = > {
throw 'baz'
})
setTimeout(() = > {
console.log(p10)
}, 0);
Copy the code
If an error value is returned, the rejection action as above is not triggered, but the error object is wrapped in a resolution contract
let p11 = p1.then(null.() = > {
return Error('qux')})setTimeout(() = > {
console.log(p11)
}, 0);
Copy the code
Promise.prototype.catch()
This method is used to add a reject handler to a contract, which accepts only one parameter: onReject. Call 'promise.prototype. Then (null, onRejected)'Copy the code
In return for new contract instances, promise.prototype.catch () is the same as onRejected for promise.prototype.then ()
let p1 = new Promise(() = > {});
let p2 = p1.catch();
setTimeout(() = > {
console.log(p1) // Promise {<pending>}
}, 0);
setTimeout(() = > {
console.log(p2) // Promise {<pending>}
}, 0);
setTimeout(() = > {
console.log(p1 === p2) // false
}, 0);
Copy the code
Promise.prototype.finally()
This method will execute regardless of the date transition to resolve or reject. This avoids redundant code in the onResolved and onRejected handlers, but it has no way of knowing whether the date status will be resolved or rejectedCopy the code
Promise. Prototype. Finally () returns the new period of instance is different from then () or catch () method returns the instance. Because onFinally is designed to be a state-independent method, it behaves as a parent contract delivery in most cases, both for resolved and rejected states
let p1 = Promise.resolve('foo');
let p2 = p1.finally();
let p3 = p1.finally(() = > undefined)
let p4 = p1.finally(() = > {})
let p5 = p1.finally(() = > Promise.resolve())
let p6 = p1.finally(() = > 'bar')
let p7 = p1.finally(() = > Promise.resolve('bar'))
let p8 = p1.finally(() = > Error('qux'))
setTimeout(() = > {
console.log(p1) //Promise {<fulfilled>: "foo"}
console.log(p2) //Promise {<fulfilled>: "foo"}
console.log(p3) //Promise {<fulfilled>: "foo"}
console.log(p4) //Promise {<fulfilled>: "foo"}
console.log(p5) //Promise {<fulfilled>: "foo"}
console.log(p6) //Promise {<fulfilled>: "foo"}
console.log(p7) //Promise {<fulfilled>: "foo"}
console.log(p8) //Promise {<fulfilled>: "foo"}
}, 0);
Copy the code
If it returns a pending date, or if onFinally throws an error (showing that it threw an error or returning a reject date), the corresponding expiration (pending or rejected) will be returned.
let p9 = p1.finally(() = > new Promise(() = > {}))
let p10 = p1.finally(() = > Promise.reject())
setTimeout(() = > {
console.log(p9) //Promise {<pending>}
console.log(p10) //Promise {<rejected>: undefined}
}, 0);
let p11 = p1.finally(() = > {throw 'baz'});
setTimeout(() = > {
console.log(p11) //Promise {<rejected>: "baz"}
}, 0);
Copy the code
It is unusual to return a pending contract, because once the contract is resolved, the new contract will still be passed to the original one as is
let p1 = Promise.resolve('foo');
let p2 = p1.finally(() = > {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('bar')},100); })})setTimeout(() = > {
console.log(p2) //Promise {<pending>}
}, 0);
setTimeout(() = > {
console.log(p2) //Promise {<fulfilled>: "foo"}
}, 200);
Copy the code
Non-reentrant date reduction methods
Js elevation does not speak human language, simply put, then method is asynchronous
let syncResolve;
let p = new Promise((resolve) = > {
syncResolve = function () {
console.log(1)
resolve()
console.log(2)
}
})
p.then(() = > {
console.log(4)
})
syncResolve()
console.log(3)
Copy the code
Similarly, onResolved onRejected, catch, finally has not reentrant
Proximity handler
That is, when multiple handlers are added to a contract, when the contract state changes, the related handlers are executed in the order in which they were added
Pass resolution values and rejection reasons
This means that resolve or reject can be passed
Reject the date and reject error handling
An error thrown in the execution function or handler of the contract causes the rejection and the corresponding error object becomes the reason for the rejectionCopy the code
let p1 = new Promise((resolve, reject) = > {
reject(Error('foo'))})let p2 = new Promise((resolve, reject) = > {
throw Error('foo')})let p3 = Promise.resolve().then(() = > {
throw Error('foo')})let p4 = Promise.reject(Error('foo'))
setTimeout(() = > {
console.log(p1)
}, 0);
setTimeout(() = > {
console.log(p2)
}, 0);
setTimeout(() = > {
console.log(p3)
}, 0);
setTimeout(() = > {
console.log(p4)
}, 0);
Copy the code
Promise.resolve().then()
The error occurs last because it requires adding handlers to the runtime message queue, i.ePromise.resolve()
A new term contract is then created and executedthen
Method, which is then added to the queue
Normally, when an error is thrown with the throw() keyword, subsequent code is not executed, but an error thrown in a contract is thrown asynchronously, so it does not prevent synchronous code execution
Promise.reject(Error('foo'))
console.log('bar')/ /
Copy the code
Asynchronous errors can only be caught through the asynchronous onRejected handler
Promise.reject(Error('foo')).catch((e) = > {
console.log(e)
})
Copy the code
While a contract is in a pending state, it can still be caught using try/catch
let p = new Promise((resolve, reject) = > {
try {
throw Error('foo')}catch(e) {
console.log(e)
}
resolve('bar')})setTimeout(() = > {
console.log(p)
}, 0);
Copy the code
The onRejected handlers for then() and catch() are semantically equivalent to try/catch. The point of both is to catch an error and isolate it without affecting normal logic. So the onRejected handler returns a resolution date after catching an asynchronous error
try/catch
console.log(1)
try{
throw Error('foo')}catch(e) {
console.log(e)
}
console.log(2)
Copy the code
onRejected
new Promise((resolve, reject) = > {
console.log(1)
reject(Error('bar'))
}).catch((e) = > {
console.log('catch', e)
}).then(() = > {
console.log('then')})Copy the code
Term linkage and term synthesis
- Term linkage: the joining of one term after another
- Term composition: To combine multiple terms into one term
function delayedResolve(str) {
return new Promise((resolve, reject) = > {
console.log(str)
setTimeout(() = > {
resolve()
}, 1000);
})
}
delayedResolve('p1').then(() = > {
return delayedResolve('p2')
}).then(() = > {
return delayedResolve('p3')
}).then(() = > {
return delayedResolve('p4')})/ / p1 after 1 s
/ / p2 after 2 s
/ / p3 after 3 s
/ / p4 after 4 s
Copy the code
This takes care of callback hell
Promise.all() and promise.race () are static methods of combining multiple term instances into a single term, and the rows of the combined term depend on the behavior of the internal term
Promise.all()
- This method takes an iterable and returns a new date
// Return a new contract
let p1 = Promise.all([
Promise.resolve(),
Promise.resolve()
])
// Elements in an iterable are converted to a contract by promise.resolve ()
let p2 = Promise.all([1.2])
// An empty iterable is equivalent to promise.resolve ()
let p3 = Promise.all([])
setTimeout(() = > {
console.log(p1)
console.log(p2)
console.log(p3)
}, 0);
Copy the code
Nothing is invalid
- Composite appointments are not resolved until all appointments have been resolved
let p = Promise.all([
Promise.resolve(),
new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve()
}, 1000); })])setTimeout(() = > {
console.log(p) //Promise {<pending>}
}, 0);
p.then(() = > {
console.log("all resolved") / / 1 s after execution
})
Copy the code
- If there is a term to be determined, then the composite term is also determined
// Used for pending state
let p1 = Promise.all([new Promise(() = > {}), Promise.resolve()])
setTimeout(() = > {
console.log(p1) // Promise {<pending>}
}, 0);
Copy the code
- If there is a term that is rejected, then the composite term is also rejected
// Result in rejection
let p2 = Promise.all([
Promise.resolve(),
Promise.reject(),
Promise.resolve()
])
setTimeout(() = > {
console.log(p2)
}, 0);
Copy the code
- If all terms are resolved, the solution of the synthesized term is an array containing the solution values of the term, in iterator order
let p1 = Promise.all([
Promise.resolve(3),
Promise.resolve(),
Promise.resolve(4)
])
p1.then((val) = > {
console.log(val) //[3, undefined, 4]
})
Copy the code
- If a fixed-term contract is rejected, the first term will use its own reasons as the reason for the refusal of the composite term, and the subsequent terms will not affect the reason for the refusal of the final term. However, this does not affect the normal rejection of all inclusive contracts
let p1 = Promise.all([
Promise.reject(3),
new Promise((resolve, reject) = > {
setTimeout(() = > {
reject()
}, 1000);
})
])
p1.catch((reason) = > {
console.log(reason) / / 3
})
Copy the code
Promise.race()
- Parameter takes an iterable and returns a new date
- The term mirror of a set of sets is returned first whose term mirror results first
let p1 = Promise.race([
Promise.resolve(),
Promise.resolve()
])
// The iterable will be converted by promise.resolve ()
let p2 = Promise.race([3.4])
// Empty iterables are equivalent to new Promise(() => {})
let p3 = Promise.race([])
setTimeout(() = > {
console.log(p1) // Promise {<fulfilled>: undefined}
console.log(p2) // Promise {<fulfilled>: 3}
console.log(p3) // Promise {<pending>}
}, 0);
Copy the code
Nothing is invalid
- The term mirror of a set of sets is returned first whose term mirror results first
// Resolve occurs first, rejection after timeout is ignored
let p1 = Promise.race([
Promise.resolve(3),
new Promise((resolve, reject) = > {
setTimeout(() = > {
reject()
}, 1000); })])// Reject occurs first, and resolution after timeout is ignored
let p2 = Promise.race([
Promise.reject(3),
new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve()
}, 1000); })])// Who decides whose mirror is returned first
let p3 = Promise.race([
Promise.resolve(5),
Promise.resolve(6),
Promise.resolve(7)])setTimeout(() = > {
console.log(p1) // Promise {<fulfilled>: 3}
console.log(p2) // Promise {<rejected>: 3}
console.log(p3) // Promise {<fulfilled>: 5}
}, 0);
Copy the code
- If the first fixed term is rejected, it will be the reason for rejecting the composite term, and the subsequent rejected terms will not affect the reason for rejecting the final term. However, it does not affect the normal rejection operations of all inclusive terms. Similar to promise.all (), the synthesized date silently processes all inclusive term rejection operations
let p = Promise.race([
Promise.reject(3),
new Promise((resolve, reject) = > {
reject() // This rejection is processed silently
}, 1000)
])
p.catch((reason) = > {
console.log(reason)/ / 3
})
Copy the code
Futures extended
There are two main extensions
- Futures to cancel
- Progress of the track
Futures to cancel
- Third party library: Bluebird
- Self-implementation: Cancel token
Start by implementing a basic instance of the CancelToken class
class CancelToken{
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) = > {
cancelFn(resolve)
})
}
}
Copy the code
This class of wrapper exposes the solution to the cancelFn parameter for a period of time. External code can pass a function to the constructor to control when the date can be cancelled.
<button id="start">Start</button>
<button id="cancel">Cancel</button>
<script>
class CancelToken{
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) = > {
cancelFn(() = > {
console.log("delay cancelled")
resolve()
})
})
}
}
const startBtn = document.querySelector("#start")
const cancelBtn = document.querySelector("#cancel")
function cancellableDelayResolve(delay) {
console.log("set delay")
return new Promise((resolve, reject) = > {
const id = setTimeout(() = > {
console.log("delayed resolve")
resolve()
}, delay)
const cancelToken = new CancelToken((cancelCallback) = > {
cancelBtn.addEventListener("click", cancelCallback)
})
cancelToken.promise.then(() = > {
clearTimeout(id)
})
})
}
startBtn.addEventListener("click".() = > {
cancellableDelayResolve(3000).then(() = > {
console.log("I did it.")})})</script>
Copy the code
Notice of progress of the contract
Contracts in execution may have a number of discrete “phases” that must be passed before final resolution. There are situations where it is useful to monitor the progress of the contract execution, but ES6 does not support progress tracking and we can extend the implementation ourselves
Extend the Promise class by adding a notify() method to it
class TrackablePromise extends Promise {
constructor(executor) {
const notifyHandlers = [];
super((resolve, reject) = > {
return executor(resolve, rejecty, (status) = > {
notifyHandlers.map((handler) = > {
return handler(status)
})
})
})
this.notifyHandlers = notifyHandlers;
}
notify(notifyHandler) {
this.notifyHandles.push(notifyHandler)
return this}}Copy the code
I don’t get it. I’ll study it later