1. Asynchronous programming
- Asynchronous behavior is designed to optimize computationally expensive and time-consuming operations.
- While waiting for other operations to complete, the system remains stable even when other instructions are run.
- Asynchronous operations do not have to be computationally expensive or time-consuming and can be used at any time if you do not want to block the thread while waiting for an operation.
1. Synchronous and asynchronous
-
Synchronous behavior corresponds to sequential execution of processor instructions in memory.
-
Asynchronous behavior is similar to a system interrupt, in which an entity outside the current process can initiate code execution.
-
Asynchronous operations are necessary because it is not feasible to force a long wait for an operation.
let x = 3 setTimeout(() = > x = x + 4.1000) Copy the code
- When will X be updated? How to notify?
2. The old asynchronous programming model
- Previously, only callback functions were supported to indicate asynchronous completion.
- Need for deeply nested callback functions, hence “callback hell”
1. Asynchronous return value
-
How to pass the return value of the asynchronous setTimeout operation to where it is needed?
-
Traditionally, you need a callback that contains the code to use the asynchronous return value (as the callback argument)
function double(value, callback) { setTimeout(() = > callback(value*2), 1000) } double(3.(x) = > console.log('I was given: ${x}')) // I was Given 6 (1000ms later) Copy the code
2. Failure handling
- Successful callback and failed callback
function double(value, success, failure) {
setTimeout(() = > {
try {
if (typeofvalue ! = ='number') {
throw 'Must provide number as first argument';
}
success(2 * value);
} catch(e) { failure(e); }},1000);
}
const successCallback = (x) = > console.log(`Success: ${x}`);
const failureCallback = (e) = > console.log(`Failure: ${e}`);
double(3, successCallback, failureCallback);
double('b', successCallback, failureCallback);
// Success: 6 (about 1000 ms later)
// Failure: Must provide number as first argument (about 1000 milliseconds later)
Copy the code
- The callback must be defined when the asynchronous operation is initialized. The return value of an asynchronous function exists only for a short period of time and can only be received by a callback that is prepared to take this short-lived value as an argument.
3. Nested asynchronous functions
- Nightmare nesting š
2, futures
- A promise is a promise.
1. Promise/A+ specification
- ES6 perfect support
2. Basis of the Contract
- The reference type Promise can be instantiated with the new operator
- You need to pass in an executor function as an argument
1. Schedule state machine
- When you pass a Promise instance to console.log(), console output indicates that the instance is in a pending state
- Three states
- Pending
- The initial state, settled (settled), can be settled (settled) when settled or rejected when settled
- The status change is irreversible
- An agreement that cannot be guaranteed must be removed from the pending state
- This is a big pity, which is sometimes called resolved.
- I don’t like it!
- Pending
- The contract state is private and cannot be externally detected. To avoid reading the contract state, the contract object is handled synchronously.
- The status of the contract cannot be modified externally.
2. Resolve values, rejection reasons, and contract use cases
- Futures USES
- An asynchronous operation is abstractly represented, and the contract state indicates whether the contract has completed. This state is useful information.
- The asynchronous operation encapsulated by the contract actually generates a value that the program can access when it expects the contract state to change.
- To satisfy both use cases, each term contract has a private internal value whenever the state switches to cash. There is a private internal reason for each contract to be switched to rejection. The default is undefined
3. Control the contract state by executing functions
-
Resolve () switches the state to cash, reject() to reject
let p1 = new Promise((resolve, reject) = > resolve()); setTimeout(console.log, 0, p1); // Promise <fulfilled> let p2 = new Promise((resolve, reject) = > reject()); setTimeout(console.log, 0, p2); // Promise <fulfilled> // Uncaught error (in promise) Copy the code
-
Regardless of whether the resolve() or reject() states are called, the state transition is irrevocable, and changing the state silently fails
-
To avoid being stuck in the convention state, you can exit at a regular time.
4, Promise. Resolve ()
-
The term does not have to be in a pending state to begin with; a resolved term can be instantiated using the promise.resolve () static method.
let p1 = new Promise((resolve, reject) = > resolve()) let p2 = Promise.resolve() Copy the code
-
The value of the resolved term corresponds to the first argument passed to promise.resolve ()
setTimeout(console.log, 0.Promise.resolve()); // Promise <fulfilled>: undefined setTimeout(console.log, 0.Promise.resolve(3)); // Promise <fulfilled>: 3 // Superfluous arguments are ignored setTimeout(console.log, 0.Promise.resolve(4.5.6)); // Promise <fulfilled>: 4 Copy the code
-
If the argument passed is itself a term, it behaves like an empty wrapper, so to speak, an idempotent method
let p = Promise.resolve(7) p === Promise.resolve(p) p === Promise.resolve(Promise.resolve(p) Copy the code
-
This idempotence will preserve the state of the input contract
let p = new Promise(() = > {}) setTimeout(console.log, 0, p) // Promise <pending> setTimeout(console.log, 0.Promise.resolve(p)) // Promise <pending> setTimeout(console.log,0,p === Promise.resolve(p)) // true Copy the code
-
You can wrap any out-of-date value, contain an error object, and convert it to a resolved term.
let p = Promise.resolve(new Error('foo')); setTimeout(console.log, 0, p); // Promise <fulfilled>: Error: foo Copy the code
5, Promise. Reject ()
-
Similar to promise.resolve (), promise.reject () instantiates a rejected term and throws an asynchronous error (cannot be caught by a try/catch, only by a rejection handler)
let p1 = new Promise((resolve, reject) = > reject()) let p2 = Promise.reject() Copy the code
-
The rejection reason is the first argument passed to promise.reject ()
let p = Promise.reject(3); setTimeout(console.log, 0, p); // Promise <rejected>: 3 p.then(null.(e) = > setTimeout(console.log, 0, e)); / / 3 Copy the code
-
If an incoming date object, the date becomes a reason for its return rejection
setTimeout(console.log, 0.Promise.reject(Promise.resolve())); // Promise <rejected>: Promise <resolved> Copy the code
- Promise.resolve() preserves the incoming state
- Promise.reject() returns failure no matter what is passed
Duality of synchronous/asynchronous execution
try {
throw new Error('foo');
} catch (e) {
console.log(e); // Error: foo
}
try {
Promise.reject(new Error('bar'));
} catch (e) {
console.log(e);
}
// Uncaught (in promise) Error: bar
Copy the code
- The reason the try/catch synchronous code does not catch errors thrown by the contract is that it does not catch errors in asynchronous mode.
- The real nature of contracts: They are synchronous objects (used in synchronous execution mode), but also the medium of asynchronous execution mode
3. Instance method of contract
- A contract instance method can access data returned by an asynchronous operation, process the results of the contract’s successes and failures, evaluate the contract continuously, or add code that will execute only if the contract enters the terminating state.
1. Implement the Thenable interface
-
Asynchronous structure, any object has then() method, implementation of Thenable interface
class MyThenable { then(){}}Copy the code
-
The Promise type implements the Thenable interface. This simplified interface makes the Thenable interface more concrete than the interface or type definition that TypeScript gets in other packages.
2, Promise. Prototype. Then ()
-
The main method for adding handlers for approximately one instance
-
Then () method
- Accept a maximum of two parameters onResolved and onRejected handlers
- Both are optional and, if offered, will be executed when the terms enter the “cashed” and “rejected” states respectively.
function onResolved(id) { setTimeout(console.log, 0, id, 'resolved')}function onRejected(id) { setTimeout(console.log, 0, id, 'rejected')}let p1 = new Promise((resolve, reject) = > { setTimeout(resolve, 3000)})let p2 = new Promise((resolve, reject) = > { setTimeout(reject, 3000) }) p1.then(() = > onResolved('p1'), () = > onRejected('p1')) p2.then(() = > onResolved('p2'), () = > onRejected('p2')) / / after 3 s // p1 resolved // p2 rejected Copy the code
- If only the onRejected handler is provided for the contract, null is passed as the first parameter. This helps to avoid creating redundant objects in memory.
function onResolved(id) { setTimeout(console.log, 0, id, 'resolved'); } function onRejected(id) { setTimeout(console.log, 0, id, 'rejected'); } let p1 = new Promise((resolve, reject) = > { setTimeout(resolve, 3000)})let p2 = new Promise((resolve, reject) = > { setTimeout(reject, 3000)});// Non-function handlers are silently ignored and not recommended p1.then('gobbeltygook'); // Do not pass the onResolved code p2.then(null.() = > onRejected('p2')); // p2 Rejected (3 ē§) Copy the code
- The promise.prototype.then () method returns a new contract instance
let p1 = new Promise(() = > {}) let p2 = p1.then() setTimeout(console.log, 0, p1) //Promise <pending> setTimeout(console.log, 0, p1) //Promise <pending> setTimeout(console.log, 0, p1===p2) // false Copy the code
- This new contract instance is built based on the return value of the onResolved handler.
- The return value of this handler is generated by the promise.resolve () wrapper.
- If this handler is not provided, promise.resolve () wraps the value after the previous contract resolution.
- If there is no explicit return statement, promise.resolve () wraps the default return value undefined
let p1 = Promise.resolve('foo') // If then() is called without passing a handler, it is passed as is let p2 = p1.then() setTimeout(console.log, 0, p2) // Promise <fulfilled>: foo Return undefined without an explicit return statement let p3 = p1.then(() = > undefined) let p4 = p1.then(() = > {}) let p5 = p1.then(() = > Promise.resolve()) / /... Promise
: undefined // If there is an explicit return value, the value is wrapped let p6 = p1.then(() = > 'bar') let p7 = p1.then(() = > Promise.resolve('bar')) / /... Promise: bar // promise.resolve () preserves the return date let p8 = p1.then(() = > new Promise(() = >{})) let p9 = p1.then(() = > Promise.reject()) setTimeout(console.log, 0, p8) // Promise <pending> setTimeout(console.log, 0, p9) // Promise <rejected>: undefined // Throwing an exception returns the rejected contract let p10 = p1.then(() = > {throw 'baz'}) // Uncaught (in promise) baz setTimeout(console.log, 0, p10) // PromiseĀ <rejected>: baz // Returning an error value does not trigger the above rejection action but is wrapped in a resolution contract let p11 = p1.then(() = > Error('qux')) setTimeout(console.log, 0, p11) // PromiseĀ <fulfilled>: Error: qux Copy the code- The onRejected handler is similar. The value returned by the onRejected handler is also wrapped in promise.resolve ().
- The onRejected handler task is to catch asynchronous errors. Using an exception that does not throw is a contract compliant behavior and should return a resolution contract.
let p1 = Promise.reject('foo') // Calling then() without passing handlers is passed back as is let p2 = p1.then() // Uncaught (in promise) foo setTimeout(console.log, 0, p2) // PromiseĀ <rejected>: foo Return undefined without an explicit return statement let p3 = p1.then(null.() = > undefined) let p4 = p1.then(null.() = > {}) let p5 = p1.then(null.() = > Promise.resolve()) / /... Promise
: undefined // If there is an explicit return value, the value is wrapped let p6 = p1.then(null.() = > 'bar') let p7 = p1.then(null.() = > Promise.resolve('bar')) / /... Promise: bar // promise.resolve () preserves the return date let p8 = p1.then(null.() = > new Promise(() = >{})) let p9 = p1.then(null.() = > Promise.reject()) setTimeout(console.log, 0, p8) // Promise <pending> setTimeout(console.log, 0, p9) // Promise <rejected>: undefined // Throwing an exception returns the rejected contract let p10 = p1.then(null.() = > {throw 'baz'}) // Uncaught (in promise) baz setTimeout(console.log, 0, p10) // PromiseĀ <rejected>: baz // Returning an error value does not trigger the above rejection action but is wrapped in a resolution contract let p11 = p1.then(null.() = > Error('qux')) setTimeout(console.log, 0, p11) // PromiseĀ <fulfilled>: Error: qux Copy the code
3, Promise. Prototype. The catch ()
-
Add a reject handler to the contract, accepting the onRejected handler as a parameter.
-
Promise.prototype. Then (null, onRejected)
let p = Promise.reject() let onRejected = function(e) { setTimeout(console.log, 0.'rejected') } p.then(null, onRejected) // rejected p.catch(onRejected) // rejected Copy the code
-
Promise.prototype.catch() returns a new contract
let p1 = new Promise(() = > {}) let p2 = p1.catch() setTimeout(console.log, 0, p1) //Promise <pending> setTimeout(console.log, 0, p1) //Promise <pending> setTimeout(console.log, 0, p1===p2) //false Copy the code
-
The new contract behaves like the onRejected handler for promise.prototype.then ().
4, Promise. Prototype. Finally ()
-
Use to add an onFinally handler to the contract. This handler is executed when the contract transitions to a resolved or rejected state.
-
You can avoid redundant code in the onResolved and onRejected handlers
-
The onFinally handler has no way of knowing whether a contract state is resolved or rejected, so it is mostly used to clean up code.
let p1 = Promise.resolve() let p2 = Promise.resolve() let onFinally = function(e) { setTimeout(console.log, 0.'Finally') } p1.finally(onFinally) // Finally p2.finally(onFinally) // Finally Copy the code
-
Promise. Prototype. Finally () returns a new futures
let p1 = new Promise(() = > {}) let p2 = p1.finally() setTimeout(console.log, 0, p1) //Promise <pending> setTimeout(console.log, 0, p1) //Promise <pending> setTimeout(console.log, 0, p1===p2) //false Copy the code
-
This new contract instance is different from the one returned by then() or catch(). Is a state-independent method that refers to the passing of a parent contract.
let p1 = Promise.resolve('foo') // pass it back 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')) / /... Promise
: foo Copy the code -
If a pending contract is returned, or an onFinally handler throws an error, the corresponding contract is returned (pending or rejected)
// promise.resolve () preserves the return date let p9 = p1.finally(() = > new Promise(() = > {})) let p10 = p1.finally(() = > Promise.reject()) // Uncaught (in promise): undefined setTimeout(console.log, 0, p9) // Promise <pending> setTimeout(console.log, 0, p10) // Promise <rejected>: undefined // Throwing an exception returns the rejected contract let p11 = p1.finally(() = > {throw 'baz'}) // Uncaught (in promise) baz setTimeout(console.log, 0, p11) // PromiseĀ <rejected>: baz Copy the code
-
It is unusual to return the pending contract, because once the contract is resolved, the new contract will be passed to the original one as is.
let p1 = Promise.resolve('foo') let p2 = p1.finally(() = > { new Promise((resolve, reject) = > { setTimeout(() = > resolve('bar'), 100)})})setTimeout(console.log, 0, p2) // Promise <pending> setTimtout(() = > setTimeout(console.log, 0, p2), 200) / / ms after 200 // Promise <fulfilled>: foo Copy the code
5. Non-reentrant contract method
-
When a contract enters a settled state, the handlers associated with that state are only scheduled, not executed immediately. Subsequent synchronization code must be executed before the handler.
-
This is true even if the term is initially associated with an additional handler and is called “non-reentrant”
// Create a settlement date let p = Promise.resolve() // Add a resolution handler p.then(() = > console.log('onResolved handler')) console.log('then() returns') // then() returns // onResolved handler Copy the code
let synchronousResolve; let p = new Promise((resolve) = > { synchronousResolve = function() { console.log('1: invoking resolve()') resolve() console.log('2: resolve() returns') } }) p.then(() = > console.log('4: then() handler executes')) synchronousResolve() console.log('3 synchronousResolve() returns') / / 1234 Copy the code
-
Non-reentrant applies to onResolved/onRejected handlers, catch() handlers and finally() handlers.
6. Execution sequence of adjacent handlers
-
If more than one handler is added, when the contract changes, the related handlers are executed in the order they were added. Then () Catch () or finally()
let p1 = Promise.resolve() let p2 = Promise.reject() p1.then(() = > setTimeout(console.log, 0.1)) p1.then(() = > setTimeout(console.log, 0.2)) / / 1 / / 2 p2.then(null.() = > setTimeout(console.log, 0.3)) p2.then(null.() = > setTimeout(console.log, 0.4)) / / 3 / / 4 p2.catch(() = > setTimeout(console.log, 0.5)) p2.catch(() = > setTimeout(console.log, 0.6)) / / 5 / / 6 p1.finally(() = > setTimeout(console.log, 0.7)) p1.finally(() = > setTimeout(console.log, 0.8)) / / 7 / / 8 Copy the code
7. Pass resolution values and rejection reasons
-
Settle state, date provides resolution value or rejection reason for further operation.
-
The resolved value and the reason for rejection are taken as the first arguments to resolve() and reject(), respectively
let p1 = new Promise((resolve, reject) = > resolve('foo')); p1.then((value) = > console.log(value)); // foo let p2 = new Promise((resolve, reject) = > reject('bar')); p2.catch((reason) = > console.log(reason)); // bar Copy the code
-
Promise.resolve() and promise.reject () receive the resolution value and the reason for rejection when called. It’s also passed to the handler
let p1 = Promise.resolve('foo'); p1.then((value) = > console.log(value)); // foo let p2 = Promise.reject('bar'); p2.catch((reason) = > console.log(reason)); // bar Copy the code
8. Reject contract and reject error handling
-
Like throw(), which represents an interrupt, throwing an error in a handler will result in a rejection.
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, 0, p1); // Promise <rejected>: Error: foo setTimeout(console.log, 0, p2); // Promise <rejected>: Error: foo setTimeout(console.log, 0, p3); // Promise <rejected>: Error: foo setTimeout(console.log, 0, p4); // Promise <rejected>: Error: foo // There are also four uncaught errors Copy the code
-
The reason can be arbitrary, but it is best to use the error object uniformly.
-
The error promise.resolve ().then() is thrown last because another term will be created
-
After an error is thrown by throw() in synchronization, subsequent statements are not continued.
-
An error thrown by throw() in asynchrony does not prevent the runtime from continuing to execute synchronous instructions
-
Asynchronous errors can only be caught through the asynchronous onRejected handler
/ / right Promise.reject(Error('foo')).catch((e) = > {}) / / error try{ Promise.reject(Error('foo')) }.catch(e){} Copy the code
-
You can still use try/catch to catch errors in the executing function before resolving or rejecting it.
let p = new Promise((resolve, reject) = > { try { throw Error('foo')}catch(e) {} resolve('bar')})setTimeout(console.log, 0, p); // promise <fulfilled>: bar Copy the code
-
The onRejected handlers for then() and catch() are semantically equivalent to try/catch
- The point of departure is to catch and isolate errors without affecting normal logic
- So the task of the onRejected handler should be to return a resolution date after catching an asynchronous error
console.log('begin synchronous execution'); try { throw Error('foo'); } catch (e) { console.log('caught error', e); } console.log('continue synchronous execution'); // begin synchronous execution // caught error Error: foo // continue synchronous execution new Promise((resolve, reject) = > { console.log('begin asynchronous execution'); reject(Error('bar')); }).catch((e) = > { console.log('caught error', e); }).then(() = > { console.log('continue asynchronous execution'); }); // begin asynchronous execution // caught error Error: bar // continue asynchronous execution Copy the code
4, about the chain and about the synthesis of period
- Term linkage is the splicing of one term after another.
- Term composition is the combination of multiple terms into one term
1, about the chain
-
Because each contract instance method (then(), catch(), finally()) returns a new contract object, the new contract object in turn has its own instance method.
let p = new Promise((resolve, reject) = > { console.log('first'); resolve() }) p.then(() = >console.log('second')) .then(() = >console.log('third')) .then(() = >console.log('fourth')) // first second third fourth Copy the code
- The result is a series of synchronization tasks, equivalent to synchronization functions.
-
Truly executing an asynchronous task allows each executor to return an instance of the contract and each subsequent contract to wait for the previous contract.
let p1 = new Promise((resolve, reject) = > { console.log('p1') setTimeout(resolve, 1000) }) p1.then(() = > new Promise(resolve, reject)=> { console.log('p2') setTimeout(resolve, 1000) }).then(() = > new Promise(resolve, reject)=> { console.log('p3') setTimeout(resolve, 1000) }).then(() = > new Promise(resolve, reject)=> { console.log('p4') setTimeout(resolve, 1000)})/ / p1 after 1 s / / p2 after 2 s / / p3 after 3 s / / p4 after 4 s Copy the code
-
Go ahead to the factory function
function delayedResolve(str) { return new Promise((resolve, reject) = > { console.log(str) setTimeout(resolve, 1000) }) } delayedResolve('p1') .then(() = > delayedResolve('p2')) .then(() = > delayedResolve('p3')) .then(() = > delayedResolve('p4')) Copy the code
-
Then (), catch(), and finally() all return dates
let p = new Promise((resolve, reject) = > { console.log('initial promise rejects'); reject(); }); p.catch(() = > console.log('reject handler')) .then(() = > console.log('resolve handler')) .finally(() = > console.log('finally handler')); // initial promise rejects // reject handler // resolve handler // finally handler Copy the code
2. Schedule chart
-
A term can have any number of handlers, and the structure of directed acyclic graphs can be constructed.
-
Each contract is a node in the diagram, and the handler added using the instance method is a directed node. The direction of the diagram is the order in which contracts are resolved or rejected.
// A / / / / // B C // / \ / // D E F G let A = new Promise((resolve, reject) = > { console.log('A'); resolve(); }); let B = A.then(() = > console.log('B')); let C = A.then(() = > console.log('C')); B.then(() = > console.log('D')); B.then(() = > console.log('E')); C.then(() = > console.log('F')); C.then(() = > console.log('G')); Copy the code
-
The output statement is a sequential traversal of the binary tree. They are executed in the order they were added.
3, promise.all () and Promise().race()
1, Promise. All ()
-
Promise.all() Static methods create appointments that are resolved after a set of appointments has been resolved. Receiving an iterable returns a new date.
let p1 = Promise.all([ Promise.resolve(), Promise.resolve() ]) // Elements in an iterable are converted to a contract by promise.resolve () let p2 = Promise.all([3.4]) // An empty iterable is equivalent to promise.resolve () let p3 = Promise.all([]) / / is invalid let p4 = Promise.all() // TypeError: ... Copy the code
-
The composite term is resolved only after each contained term has been resolved
let p = Promise.all([ Promise.resolve(), new Promise((resolve, reject) = > setTimeout(resolve, 1000)));setTimeout(console.log, 0, p); // Promise <pending> p.then(() = > setTimeout(console.log, 0.'all() resolved! ')); // all() resolved! (About 1 second later) Copy the code
-
If at least one contained term is to be determined, the composite term is also to be determined
-
If an included term is rejected, the composite term is also rejected
// To be determined forever let p1 = Promise.all([new Promise(() = >{}));setTimeout(console.log, 0, p1); // Promise <pending> // A rejection will result in a final rejection let p2 = Promise.all([ Promise.resolve(), Promise.reject(), Promise.resolve() ]); setTimeout(console.log, 0, p2); // Promise <rejected> // Uncaught (in promise) undefined Copy the code
-
If both are resolved, the solution values of the synthesized term are all the arrays containing the solution values of the term, in iterator order.
let p = Promise.all([ Promise.resolve(3), Promise.resolve(), Promise.resolve(4) ]) p.then((values) = > setTimeout(console.log,0,values)) // [3, undefined, 4] Copy the code
-
If a fixed-term contract is rejected, the first fixed-term contract will use its own reasons as the reason for the refusal of the composite term contract, and the subsequent rejection will not affect the reason for the refusal of the final term contract
-
Does not affect the normal rejection operations of all inclusive terms, and the synthesized date silently processes all rejection operations of inclusive terms.
// Although only the first contract rejection reason will enter // Reject handler, second contract reject also // Will be silently processed, no errors will run away let p = Promise.all([ Promise.reject(3), new Promise((resolve, reject) = > setTimeout(reject, 1000))); p.catch((reason) = > setTimeout(console.log, 0, reason)); / / 3 // There are no unhandled errors Copy the code
2, Promise. Race ()
-
The promise.race () static method returns a wrapper term that is the first mirror of a set of collections to resolve or reject the term.
let p1 = Promise.race([ Promise.resolve(), Promise.resolve() ]) // Elements in an iterable are converted to a contract by promise.resolve () let p2 = Promise.race([3.4]) // Empty iterables are equivalent to new Promise(()=> {}) let p3 = Promise.race([]) / / is invalid let p4 = Promise.race() // TypeError: ... Copy the code
-
Settlement or rejection is not treated differently. The first settled term, promise.race (), wraps its settlement value or rejection reason and returns the new term.
// Resolve occurs first, rejection after timeout is ignored let p1 = Promise.race([ Promise.resolve(3), new Promise((resolve, reject) = > setTimeout(reject, 1000)));setTimeout(console.log, 0, p1); // Promise <fulfilled>: 3 // Reject occurs first, and resolution after timeout is ignored let p2 = Promise.race([ Promise.reject(4), new Promise((resolve, reject) = > setTimeout(resolve, 1000)));setTimeout(console.log, 0, p2); // Promise <rejected>: 4 // The iteration order determines the final order let p3 = Promise.race([ Promise.resolve(5), Promise.resolve(6), Promise.resolve(7)]);setTimeout(console.log, 0, p3); // Promise <fulfilled>: 5 Copy the code
-
If a contract is rejected first, as long as he is the first to be confirmed, it will be the reason for rejection. Subsequent rejection does not affect the final reason for rejection. However, normal reject operations will not be affected. The synthesized date silently processes all reject operations that contain terms.
4. Serial phase contract synthesis
-
Synthesis of analogous functions
function addTwo(x) { return x + 2; } function addThree(x) { return x + 3; } function addFive(x) { return x + 5; } function addTen(x) { return Promise.resolve(x) .then(addTwo) .then(addThree) .then(addFive); } addTen(8).then(console.log); / / 18 // Array.prototype.reduce() function addTwo(x) { return x + 2; } function addThree(x) { return x + 3; } function addFive(x) { return x + 5; } function addTen(x) { return [addTwo, addThree, addFive] .reduce((promise, fn) = > promise.then(fn), Promise.resolve(x)) } addTen(8).then(console.log); / / 18 // Bring up the public function function addTwo(x) { return x + 2; } function addThree(x) { return x + 3; } function addFive(x) { return x + 5; } function compose(. fns) { return (x) = > fns.reduces((promise, fn) = > promise.then(fn), Promise.resolve(x)) } let addTen = compose(addTwo, addThree, addFive) addTen(8).then(console.log); / / 18 Copy the code
5. Term contract expansion
- Third party term library does not have term cancellation and progress tracking
1. Cancellation of the contract
-
With the “cancel token “, which is essentially a layer of temporary encapsulation, cancellation is triggered.
class CancelToken { constructor(cancelFn) { this.promise = new Promise((resolve, reject) = > { cancelFn(resolve) }) } } function cancelableDelayResolve() {}const id = setTimeout(() = > { resolve() }, delay) const cancelToken = new CancelToken((cancelCb) = > { xxxx.addEventListener('click', cancelCb) }) cancelToken.promise.then(() = >clearTimeout(id)) Copy the code
2. Schedule notice
-
Schedule execution progress by extending the monitoring period.
-
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, reject, (status) = > { notifyHandlers.map((handler) = > handler(status)) }) }) this.notifyHandlers = notifyHandlers } notify(notifyHandler) { this.notifyHandlers.push(notifyHandler) return this}}// Use notify when executing functions. let p = new TrackablePromise((resolve, reject, notify) = > { function countdown(x) { if(x > 0) { notify(`The ${20 * x}% remaining`) setTimeout(() = > countdown(x - 1), 1000)}else { resolve() } } countdown(5) }) p.notify((x) = > setTimeout(console.log, 0.'progress:', x)) p.then(() = > setTimeout(console.log, 0 , 'completed')) // (about 1 second later) 80% remaining // (about 2 seconds later) 60% remaining // (about 3 seconds later) 40% remaining // (about 4 seconds later) 20% remaining // (about 5 seconds later) completed Copy the code
3. Asynchronous functions
-
Async /await, new in ES8, allows code written synchronously to be executed asynchronously.
/ / in the past let p = new Promise((resolve, reject) = >setTimeout(resolve, 1000.3)) p.then((x) = >console.log(x)) Copy the code
1. Asynchronous functions
1, async
-
The async keyword is used to declare asynchronous functions and can be used on function declarations, function expressions, arrow functions, and methods.
async function foo() {} let bar = async function() {} let baz = async() = > {}class Qux { async qux(){}}Copy the code
-
Async allows functions to have asynchronous characteristics, but still evaluate synchronously overall
-
Asynchronous functions still have the normal behavior of normal JavaScript functions in terms of arguments and closures
async function foo() { console.log(1) } foo() console.log(2) / / 1 / / 2 Copy the code
-
The value returned by the asynchronous function via return (undefined if there is none), which is wrapped as a date object by promise.resolve (). Asynchronous functions always return a date object. Calling this function outside the function yields the date it returns.
async function foo() { console.log(1) return 3 } foo().then(console.log) console.log(2) / / 1 2 3 // Return a date object directly async function foo() { console.log(1) return Promise.resolve(3) } foo().then(console.log) console.log(2) / / 1 2 3 Copy the code
-
The asynchronous function returns a value that is expected to implement (but not required to)the Thenable interface. Regular values are fine. If the return is an object that implements the Thenable interface, it can be “unpacked” by the handler provided to then(). Otherwise, it is considered a settled term.
// Return a raw value async function foo() { return 'foo'; } foo().then(console.log); // foo // Return an object that does not implement thenable async function bar() { return ['bar']; } bar().then(console.log); // ['bar'] // Return an out-of-date object that implements the Thenable interface async function baz() { const thenable = { then(callback) { callback('baz'); }};return thenable; } baz().then(console.log); // baz // Return a date async function qux() { return Promise.resolve('qux'); } qux().then(console.log); // qux Copy the code
-
As in a contract handler, throwing an error in an asynchronous function returns a rejected contract
async function foo() { console.log(1) throw 3 } foo.catch(console.log) console.log(2) / / 1 / / 2 / / 3 Copy the code
-
Rejection contracts are not caught by asynchronous functions
async function foo() { console.log(1) Promise.reject(3)}// Attach a rejected handler to the returned promise foo().catch(console.log) console.log(2) / / 1 / / 2 //Uncaught (in promise): 3 Copy the code
2, await
-
Await can suspend the execution of asynchronous function code and the wait period is resolved
let p = new Promise((resolve, reject) = >setTimeout(resolve, 1000.3)) p.then((x) = > console.log(x)) / / 3 // ----> async function foo() { let p = new Promise((resolve,reject) = >setTimeout(resolve, 1000.3)) console.log(await p) } foo() / / 3 Copy the code
-
The behavior of await is the same as yield. Also try to “unpack” the value of the object.
-
Await can be used alone or in expressions.
// Print foo asynchronously async function foo() { console.log(await Promise.resolve('foo')) } foo() // foo // Print bar asynchronously async function bar() { return await Promise.resolve('bar') } bar().then(console.log) // bar // baz is printed asynchronously after 1000ms async function baz() { await new Promise((resolve, reject) = > { return setTimeout(resolve, 1000)})console.log('baz') } baz() // baz (1000ms later) Copy the code
-
Await an object implementing the ThEnable interface. If it is a thenable interface object, unpack it; otherwise, the value is considered resolved
// Wait for a raw value async function foo() { console.log(await 'foo'); } foo(); // foo // Wait for an object that does not implement thenable async function bar() { console.log(await ['bar']); } bar(); // ['bar'] // Wait for a non-scheduled object that implements the Thenable interface async function baz() { const thenable = { then(callback) { callback('baz'); }};console.log(await thenable); } baz(); // baz // Wait for an appointment async function qux() { console.log(await Promise.resolve('qux')); } qux(); // qux Copy the code
-
Waiting for a synchronization operation that throws an error returns a reject period
async function foo() { console.log(1) await (() = > {throw 3}) ()console.log(4) // Will not be executed } foo().catch(console.log) console.log(2) / / 1 / / 2 / / 3 Copy the code
-
The separate promise.reject () is not caught by the asynchronous function and instead throws an uncaught error. Using await on a rejected term releases an error value.
async function foo() { console.log(1) await Promise.reject(3) console.log(4) // Will not be executed } foo().catch(console.log) console.log(2) / / 1 / / 2 / / 3 Copy the code
3. Limitations of await
- Must be used in asynchronous functions, not in top-level contexts.
- Can be defined and called immediately
- The asynchronous function nature does not extend to escape functions and await only appears directly in the definition of an asynchronous function
2. Stop and resume execution
-
Order problem
async function foo() { console.log(await Promise.resolve('foo')); } async function bar() { console.log(await 'bar'); } async function baz() { console.log('baz'); } foo(); bar(); baz(); // baz // foo // bar // baz bar foo Copy the code
-
Asynchronous functions do not contain the await keyword, just like normal functions
-
When an await is encountered at runtime, where is it recorded to suspend execution, and when the value to the right of the await becomes available, a task is pushed to the message queue, which resumes asynchronous function execution
-
Even if await follows immediately available value, the rest of the function is evaluated asynchronously
async function foo() { console.log(2) await null console.log(4)}console.log(1) foo() console.log(3) // 1, 2, 3, 4 Copy the code
- Print 1
- Call the asynchronous function foo()
- Print 2 in foo()
- Suspends execution of the await keyword in foo() and adds the task to the message queue for the immediately available value NULL
- Foo ()
- Print 3
- The synchronized thread code is complete
- The JavaScript runtime retrieves the task from the message queue and resumes asynchronous function execution
- Resume execution in foo(), await null (not used)
- Print 4 in foo()
- Foo () returns
-
If a term is followed, two tasks are actually added to the message queue and evaluated asynchronously in order to execute the asynchronous function (newer browsers only generate one asynchronous task)
async function foo() { console.log(2) // The old version added an extra step to restore the execution of foo() console.log(await Promise.resolve(8)) console.log(9)}async function bar() { console.log(4) console.log(await 6) console.log(7)}console.log(1) foo() console.log(3) bar() console.log(5) The old / / 123456789 / / 123458967 new Copy the code
-
3. Asynchronous function strategy
1. Implement sleep()
async function sleep(delay) {
return new Promise((resolve) = > setTimeout(resolve,delay))
}
async function foo() {
const t0 = Date.now()
await sleep(1500)
console.log(Date.now() - t0)
}
foo()
/ / 1502
Copy the code
2. Use parallel execution
-
Opportunity for parallel acceleration
async function randomDelay(id) { // The delay ranges from 0 to 1000 ms const delay = Math.random() * 1000; return new Promise((resolve) = > setTimeout(() = > { console.log(`${id} finished`); resolve(); }, delay)); } async function foo() { const t0 = Date.now(); await randomDelay(0); await randomDelay(1); await randomDelay(2); await randomDelay(3); await randomDelay(4); console.log(`The ${Date.now() - t0}ms elapsed`); } foo(); // 0 finished // 1 finished // 2 finished // 3 finished // 4 finished // 877ms elapsed / / a for loop async function foo() { const t0 = Date.now(); for(let i = 0; i < 5; ++i) { await randomDelay(i) } console.log(`The ${Date.now() - t0}ms elapsed`); } Copy the code
-
If there are no dependencies between the terms, asynchronous functions will also pause in order to ensure the order of execution, but the total time will also be longer. If the order is not guaranteed, you can initialize all contracts at once and wait for the results separately.
async function randomDelay(id) { // The delay ranges from 0 to 1000 ms const delay = Math.random() * 1000; return new Promise((resolve) = > setTimeout(() = > { setTimout(console.log, 0.`${id} finished`); resolve(); }, delay)); } async function foo() { const t0 = Date.now() const p0 = randomDelay(0) const p1 = randomDelay(1) const p2 = randomDelay(2) const p3 = randomDelay(3) const p4 = randomDelay(4) await p0 await p1 await p2 await p3 await p4 setTimeout(console.log, 0 ,`The ${Date.now() - t0}ms elapsed`); } foo() // 1 finished // 4 finished // 3 finished // 0 finished // 2 finished // 877ms elapsed / / a for loop async function foo() { const t0 = Date.now() const promises = Array(5).full(null).map((_, i) = >randomDelay(i)) for(const p of promises) { await p } setTimeout(console.log, 0 ,`The ${Date.now() - t0}ms elapsed`); } Copy the code
-
Although the term is not executed in order, await receives the value of each term in order
async function randomDelay(id) { // The delay ranges from 0 to 1000 ms const delay = Math.random() * 1000; return new Promise((resolve) = > setTimeout(() = > { console.log(`${id} finished`); resolve(id); }, delay)); } async function foo() { const t0 = Date.now(); const promises = Array(5).fill(null).map((_, i) = > randomDelay(i)); for (const p of promises) { console.log(`awaited The ${await p}`); } console.log(`The ${Date.now() - t0}ms elapsed`); } foo(); Copy the code
3. Serial execution of the term contract
function addTow(x) {return x + 2}
function addThree(x) {return x + 2}
function addFive(x) {return x + 5}
async function addTen(x) {
for(const fn of [addTow, addThree, addFive]) {
x = await fn(x)
}
return x
}
addTen(9).then(console.log) / / 19
Copy the code
4. Stack tracking and memory management
-
Scheduling and asynchronous functions differ greatly in memory.
function fooPromiseExecutor(resolve, reject) { setTimeout(reject, 1000.'bar')}function foo() { new Promise(fooPromiseExecutor) } foo() // Uncaught (in promise) bar // setTimeout (async) // fooPromiseExecutor // foo Copy the code
- Some of the functions that have already returned, they still show up. There are computing and storage costs
function fooPromiseExecutor(resolve, reject) { setTimeout(reject, 1000.'bar'); } async function foo() { await new Promise(fooPromiseExecutor); } foo(); // Uncaught (in promise) bar // foo // async function (async) // foo Copy the code
- JavaScript runtime can simply store Pointers to contained functions in nested Korean elements, just like synchronous function call stacks.
- This pointer is actually stored in memory and can be used to generate stack trace information for further errors.