Preamble: The concept of asynchrony and synchronization is not discussed much, and there are many articles about it on the Internet, which is not the focus of this article.

1. Create promises

1.1 es6 newpromiseThe reference type can be passednewOperator to instantiate and create a newpromiseInstance, you need to pass in oneExecutor functionArgument, which is thrown if not passedSyntaxError.

let p = new Promise(() => {})
setTimeout(console.log,0,p); //Promise <pending>
Copy the code

2. Promise has three states:

Pending?

Resolved (solve)

The rejected (refuse)

2.1 Understanding of the three states

Pending is the initial state of a promise that is not settled or is being executed. In the pending state, a promise can be settled (resolved) or rejected (rejected). However, either state is irreversible, and there is no guarantee that a promise is out of pending state.

let p = new Promise((resolve, reject) => { resolve(); reject(); }) setTimeout(console.log,0,p) Promsie <resolved>Copy the code

2.2 The actuator function controls the change of state

Since the state of a promise is private, the state can only be changed in the promise’s executor function, whose main function is to initialize the asynchronous behavior of the promise and control the final transition of the state. The promsie state transition is controlled by two function parameters of the executor function, which are usually named resolve and reject. Calling resolve() in the executor function switches the state to Resolved, Calling Reject () switches the state to Rejected

let p1 = new Promise((resolve,reject) => resolve());
setTimeout(console.log,0,p1); //Promise <resolved>
Copy the code
let p2 = new Promise((resolve,reject) => reject());
setTimeout(console.log,0,p2); //Promise <rejected>
Copy the code

One thing to note is that in promise the executor functions are executed synchronously, because the executor function is the promise initializer

The new Promise (() = > setTimeout (the console log, 0, '1')); SetTimeout (the console. The log, 0, '2'); / / 1 / / 2Copy the code

2.3 Another way of writing

2.3.1 Promise. Resolve ()

A promise can be changed to resolved or Rejected by calling the promise.resolve () static method. These two examples are equivalent

let p1 = new Promise((resolve, reject) => resolve())
let p2 = new Promise.resolve();
Copy the code
let p = new Promise.resolve(2);
setTimeout(console.log,0,p); // promise <resolved> :2
Copy the code

If the resolve argument was originally a promise, return the promise (idempotent logic)

2.3.2 Promise. Reject ()

Similar to promise.resolve(), promise.reject() instantiates a Rejected promise and throws an asynchronous error (this cannot be caught by a try/catch, but only by a rejectd handler). The following two promise instances are the same

let p1 = new Promise((resolve, reject) => reject())
let p2 = new Promise.reject();
Copy the code
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

It is important to note that promise.reject() does not have idempotent logic like promise.resolve(). If you pass a promise object to it, this object will be used as a justification for its rejected return.

3. Instance method of promise

The methods of the Promise instance are a bridge between external synchronous code and internal asynchronous code

3.1 Promise. Prototype. Then ()

This method takes two arguments, the onResolved handler and the onRejected handler. Both parameters are optional. The onResolved handler changes to Resolved and the onRejected handler changes to Rejected.

function onResovled(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(() => onResovled('p1'), () => onRejected('p1')) p2.then(() => onResovled('p2'), () => onRejected(' P2 ')) // 3 seconds later // P1 resolved // P2Copy the code

Note:The promise.prototype.then() method returns a new Promise instance, this new Promise instance is built from the return value of the onResovled handler (the first function argument to then), (Catch is built based on the return value of the onjected handlerIn other words, the return value of the handler generates a new Promise through the promise.resolve () wrapper,2(If this handler is not available. Promise.resolve () will wrap a promise into the resolved state return value),3(If there is no explicit return statement, promise wraps the default return value undefined.)

Let p1 = promise.resolve ('foo') // let p2 = p1.then(); setTimeout(console.log,0,p2) // Promise <resolved> : foo let p3 = p1.then(() => {}); As in case 3, setTimeout(console.log,0,p3) // Promise <resolved> : undefinedCopy the code

If there is an explicit return value, promise.resolve() wraps the return value.

let  p6 = p1.then(() => 'bar')

setTimeout(console.log,0,p6) // Promise <resolved>: bar
Copy the code

Throwing an exception returns the Rejected promise:

let p10 = p1.then(() => {throw:'baz'});
setTimeout(console.log,0,p10) // Promsise <rejected>: baz
Copy the code

Throwing an error value does not trigger the Rejected behavior. Instead, it wraps the error object in a Resolved promise

let p11 = p1.then(()=> Error("qux"))
setTimeout(console.log,0,p11) // Promise <resolved: Error: qux>
Copy the code

3.2 Promise. Prototype. The catch ()

The promise.prototype.catch () method is used to add the Rejected handler to the Promise, which takes only one parameter: Prototype. Then (null,onRejected); the catch method returns a new promise instance. Differ from the then method by promising. Resolve () wraps the return value of the onjected handler.

3.3 Promise. Prototype. Finally ()

Promise. Prototype. Finally add () method is used to Promise onFinally handler, the handler when converting Promise resolved or rejected state will be executed. This method avoids redundant code in the onResolved and onRejected handlers. But the onFinally handler has no way of knowing whether the date status is Resolved or Rejected, so this method is mostly used to add cleanup code.

let p1 = Promise.resolve(); let p2 = Promise.reject(); let onFinally = function() { setTimeout(console.log, 0, 'Finally! ') } p1.finally(onFinally); // Finally p2.finally(onFinally); // FinallyCopy the code

Like the then and catch methods, the finally method returns a new promise instance: this new promise instance is different from the one returned by then() or catch(). Because onFinally is designed to be a state-independent method, in most cases it will behave as a delivery of the previous promise. This is true for the Resolved state and the Rejected state. (Compare the magnified note above)

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, 0, p2); // Promise <resolved>: foo setTimeout(console.log, 0, p3); // Promise <resolved>: foo setTimeout(console.log, 0, p4); // Promise <resolved>: foo setTimeout(console.log, 0, p5); // Promise <resolved>: foo setTimeout(console.log, 0, p6); // Promise <resolved>: foo setTimeout(console.log, 0, p7); // Promise <resolved>: foo setTimeout(console.log, 0, p8); // Promise <resolved>: fooCopy the code

4. Pass resolution values and rejection reasons

When the Promise state changes to Resolved, there is a private internal value, and when the State changes to Rejected, there is a private internal reason. Both the value and the reason are immutable references containing the original inner value or object.

Once the settled state is reached, a promise provides its resolution value (if fulfilled) or its reason for rejection (if rejected) to the handler of the associated state. In the execution function, the resolved value and the reason for rejection are passed as the first arguments to resolve() and reject(), respectively. These values are then passed to their respective handlers as the only arguments to the onResolved or onRejected handler (the first and second arguments to the then method). The following example shows the above delivery process:

Value passed

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

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

Transmission of reasons for rejection

Throwing an error in the promise’s execution function or handler causes the state to change to Rejected, and the corresponding error object becomes a reason to reject. Therefore, each of the following promises will be rejected as a false object.

let p2 = new Promise((resolve, reject) => { throw Error('foo'); });
let p3 = Promise.resolve().then(() => { throw Error('foo'); });

setTimeout(console.log, 0, p2); // Promise <rejected>: Error: foo
setTimeout(console.log, 0, p3); // Promise <rejected>: Error: foo
Copy the code

Note: Asynchronous errors do not block the execution of synchronous code, and try/catch does not catch errors thrown by promises.

In addition:

The onRejected handlers for then() and catch() are semantically equivalent to try/catch. The starting point is to catch errors and isolate them without affecting normal logical execution. To do this, the task of the onRejected handler should be to return a resolution date after catching an asynchronous error. The following example compares synchronous error handling with asynchronous error handling:

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

5. Promise. All () and Promise. The race ()

The Promise class provides two static methods for combining multiple contract instances into a single contract: promise.all () and promise.race (). The behavior of the late composite contract depends on the behavior of the internal contract.

5.1 Promise. All ()

Promise.all() Static methods create appointments that are resolved after a set of appointments has been resolved. This static method takes an iterable and returns a new date:

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 pending, the composite term is also pending. If an contained term is rejected, then the composite term is also rejected:

/ / pending forever let p1 = Promise. All ([new Promise (() = > {})]); setTimeout(console.log, 0, p1); // Promise <pending> // let p2 = promise.all ([promise.resolve (), promise.reject (), promise.resolve ()]); setTimeout(console.log, 0, p2); // Promise <rejected> // Uncaught (in promise) undefinedCopy the code

If all terms are successfully resolved, the resolution values of the synthesized term are all arrays containing the resolution 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 the fixed-term contract is rejected, the first fixed-term contract that rejects will use its own reasons as the reason for the refusal of the composite fixed-term contract. Subsequent terms of rejection do not affect the grounds for rejection of the final terms. However, this does not affect the normal rejection of all inclusion contracts. The synthesized date silently handles all reject operations that contain the date, as shown below:

// Although only the rejection reason of the first contract will enter the // rejection process, the rejection of the second contract will also be processed silently, Let p = promise.all ([promise.reject (3), new Promise((resolve, reject) => setTimeout(reject, 1000))]); p.catch((reason) => setTimeout(console.log, 0, reason)); // 3 // No unhandled errorsCopy the code

5.2 Promise. Race ()

The promise.race () static method returns a wrapper term that is a mirror image of the first term resolved or rejected in a set of collections. This method takes an iterable and returns a new date:

let p1 = Promise.race([ Promise.resolve(), Promise.resolve() ]); // Elements in an iterable are converted to a contract by promise.resolve ()Copy the code

Promise.race() does not discriminate between resolved or rejected terms. Promise.race() wraps its settlement value or rejection reason for the first settled term, either resolved or rejected, and returns the new term:

// Resolution comes first, Let p1 = promise.race ([promise.resolve (3), new Promise((resolve, reject) => setTimeout(reject, 1000))]); setTimeout(console.log, 0, p1); // Promise <resolved>: 3 // Rejection occurs first, Let p2 = promise.race ([promise.reject (4), new Promise((resolve, reject) => setTimeout(resolve, 1000))]); setTimeout(console.log, 0, p2); // Promise <rejected>: Let p3 = promise.race ([promise.resolve (5), promise.resolve (6), promise.resolve (7)]); let p3 = promise.race ([promise.resolve (5), promise.resolve (6), promise.resolve (7)]); setTimeout(console.log, 0, p3); // Promise <resolved>: 5Copy the code

If there is a term rejection, as long as it is the first one settled, it becomes a reason to reject the composite term. Subsequent terms of rejection do not affect the grounds for rejection of the final terms. However, this does not affect the normal rejection of all inclusion contracts. Like promise.all (), the synthesized date silently handles all rejection operations that contain the date, as shown below:

// Although only the rejection reason of the first contract will enter the // rejection process, the rejection of the second contract will also be processed silently, Let p = promise.race ([promise.reject (3), new Promise((resolve, reject) => setTimeout(reject, 1000))]); p.catch((reason) => setTimeout(console.log, 0, reason)); // 3 // No unhandled errorsCopy the code

Conclusion:

To create a Promise instance, pass in an executor function named resolve and reject. Call resolve () to switch the promise state to resolved. Reject () becomes rejected. The state can also be changed directly. For example, the static method promise.resolve() is called to change the state to resolved, and promise.reject() is called to change the state to Rejected.

This is not possible in practical use, but is usually used in conjunction with the Promise instance methods (then,catch,finally) to manipulate the value of the executor function after it is executed, or an error. Both the Promise instance methods return a new Promise instance, and both then and catch wrap the return value of the corresponding handler in promise.resolve () (in effect, taking the return value as an argument to resolve). Resolve () is used to wrap the return value of the last promise in promise.resolve (). In addition, static methods of race and ALL will return a new promise instance. The returned promise instance will be resolved and will accept all of the Promise’s solved() arguments as the resolve argument of the new promise. If there is a reject, the returned promise will reject as well. The rejection is the first acceptance reason. One way to think about race is that promise.race () doesn’t discriminate between contracts that are resolved or rejected. Either settlement or rejection, as long as it is the first settled term, promise.race () wraps its settlement value or rejection reason and returns the new term.