Futures basis

ECMAScript6’s new reference type Promise can be instantiated using the new operator, which creates a new contract by passing in a function argument, also known as an executor function. If no executor function is passed in, a SyntaxError is reported.

let p = new Promise((resolve, reject) = > {})
// The actuator function takes two arguments
Copy the code

A new contract instance has three states, namely, pending, fulfilled or resolved and rejected. Pending is the initial state of the contract, in the pending state, the contract can be resolved or rejected. The transition to either state is irreversible, and there is no guarantee that the period will necessarily change from indeterminate to another state. So it’s a good idea to consider all cases in your code logic. The state of a contract is private and cannot be modified by external JS code, which deliberately encapsulates asynchronous behavior to isolate external synchronous code.

Control contract state by performing functions

Because the state of the contract is private, it can only be operated on internally, which is done in the executor function, which has two main responsibilities: initializing the asynchronous behavior of the contract and controlling the final transition of the state. The final transition of the control state is achieved by calling its two function arguments. Both function parameters are usually named resolve and reject. A call to resolve switches the state to resolve, while a call to Reject switches the state to reject and throws an error.

let p = new Promise((resolve, reject) = > resolve())
let p = new Promise((resolve, reject) = > reject())
Copy the code

Note: The code in the executor function is synchronous code. The state transition is irrevocable when either resolve or reject is called in an executor function, and continues to fail silently if the state is modified.

Promise.resolve()

A term can be converted to a settled state by calling the executor function, and a resolved term can be instantiated by calling the promise.resolve () static method. The following two contract instances are actually the same:

let p = new Promise((resolve, reject) = > resolve());
let p1 = PromiseThe resolve ();// The value of the resolved term corresponds to the first argument passed to promise.resolve (). Using this static method, virtually any value can be converted to a term.
// Superfluous arguments are ignored
// For this static method, if the parameter passed is itself a term, it behaves like an empty wrapper.
let p = PromiseResolve (1);setTimeout(console.log, 0, p === Promise.resolve (p));//true
setTimeout(console.log, 0, p === PromiseResolve (PromiseResolve (p)));//true

// The empty package will also retain the status of the incoming contract:
let p = new Promise(() = > {});
setTimeout(console.log, 0, p); //Promise<pending>
setTimeout(console.log, 0, p === Promise.resolve (p));//true

// Note: This static method can wrap any out-of-date value, including the error object, and convert it to the resolved term. Therefore, it can also lead to behavior that does not meet expectations.
let p = Promise.resolve(new Error('abc'));
setTimeout(console.log, 0, p); //Promise<resolved>: Error: abc
Copy the code

Promise.reject()

Similar to promise.resolve (), promise.reject () instantiates a rejected term and throws an asynchronous error that cannot be caught by a try/catch, but only by a rejection handler. The following two contract instances are actually the same

let p = new Promise((resolve, reject) = > reject());
let p1 = PromiseReject ();// The reason for this rejection is the first argument passed to promise.reject (), which is also passed to subsequent rejection handlers

let p = PromiseReject (3);setTimeout(console.log, 0, p); // Promise<rejected>: 3
p.then(null.(e) = > {
  setTimeout(console.log, 0, e); / / 3
})
Reject () does not apply to promise.reject (). If you pass promise.reject () a reject object, the reject object becomes the reason it returns:

setTimeout(console.log, 0.Promise.reject(Promise.resolve())); // Promise<rejected>: Promise<resolved>

Copy the code

A contract rejection error can only be caught asynchronously:

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

Instance method of a contract

The methods of a contract instance are a bridge between external synchronous code and internal asynchronous code, which can access the data returned by an asynchronous operation, process the output of a contract’s success and failure, evaluate the contract continuously, or add code that is executed only when the contract enters the terminating state. Promise.prototype.then() : This method accepts up to two arguments, the onResolved handler and the onRejected handler. Both parameters are optional and, if provided, will be executed when the contract enters the resolved and rejected states, respectively. Since the contract can only be converted once, the two operations must be mutually exclusive. If the argument passed to the then method is not a function, it is silently ignored. If you only want to provide the onRejected argument, it is best to pass NULL in the onResolved argument. The then method returns a new contract instance. The new contract instance is built on either a resolution handler or a rejection handler, as the case may be; in other words, the return value of the handler is generated by the promise.resolve () wrapper. At contract resolution, if no resolution handler is provided, promise.resolve () wraps the value after the previous contract resolution. Resolve wraps the default return value undefined if there is no explicit return statement.

// Note: By default, all console. logs are printed in the setTimeout function for presentation purposes.
let p1 = Promise.resolve('foo');
let p2 = p1.then();
let p3 = p1.then(() = > undefined);
let p4 = p1.then(() = > {});
let p5 = p1.then(() = > Promise.resolve());

console.log(p2); // Promise <resolved> : foo
console.log(p3); // Promise <resolved> : undefined
console.log(p4); // Promise <resolved> : undefined
console.log(p5); // Promise <resolved> : undefined

// If there is a displayed return value, promise.resolve will wrap that value.

let p6 = p1.then(() = > 'bar');
let p7 = p1.then(() = > Promise.resolve('bar'));
let p8 = p1.then(() = > null);

console.log(p6); // Promise <resolved> : bar
console.log(p7); // Promise <resolved> : bar
console.log(p8); // Promise <resolved> : null

let p9 = p1.then(() = > new Promise(() = > {}));
let p10 = p1.then(() = > Promise.reject());

console.log(p9); // Promise <pending>
console.log(p10); // Promise <rejected> : undefined

let p11 = p1.then(() = > { throw 'baz' }); // Throwing an exception returns the rejected contract.
console.log(p11); // Promise <rejected> : baz

// Returns an error value. Instead of throwing a rejected term, the error object is wrapped in a resolved term.

let p12 = p1.then(() = > Error('abc'));
console.log(p12); // Promise <resolved> : Error: abc
Copy the code

The onRejected handler is similar: The return value of the onRejected handler is also wrapped in promise.resolve (). This may seem counterintuitive at first, but if you think about it, the onRejected handler’s job is to catch asynchronous errors. So the onRejected handler is contract compliant if it does not throw an exception after catching an error and should return a resolved contract.

// Note: By default, all console. logs are printed in the setTimeout function for presentation purposes.
let p1 = Promise.reject('foo');
let p2 = p1.then();
let p3 = p1.then(null.() = > undefined);
let p4 = p1.then(null.() = > {});
let p5 = p1.then(null.() = > Promise.resolve());

console.log(p2); // Promise <rejected> : foo
console.log(p3); // Promise <resolved> : undefined
console.log(p4); // Promise <resolved> : undefined
console.log(p5); // Promise <resolved> : undefined

// If there is a displayed return value, promise.resolve will wrap that value.

let p6 = p1.then(null.() = > 'bar');
let p7 = p1.then(null.() = > Promise.resolve('bar'));
let p8 = p1.then(null.() = > null);

console.log(p6); // Promise <resolved> : bar
console.log(p7); // Promise <resolved> : bar
console.log(p8); // Promise <resolved> : null

let p9 = p1.then(null.() = > new Promise(() = > {}));
let p10 = p1.then(null.() = > Promise.reject());

console.log(p9); // Promise <pending>
console.log(p10); // Promise <rejected> : undefined

let p11 = p1.then(null.() = > { throw 'baz' }); // Throwing an exception returns the rejected contract.
console.log(p11); // Promise <rejected> : baz

// Returns an error value. Instead of throwing a rejected term, the error object is wrapped in a resolved term.

let p12 = p1.then(null.() = > Error('abc'));
console.log(p12); // Promise <resolved> : Error: abc
Copy the code

Promise.prototype.catch():

The promise.prototype.catch () method is used to add a rejection handler to the contract, which takes only one parameter: the onRejected handler. In fact, this method is a syntactic sugar; calling it is equivalent to calling promise.prototype. then(null, onRejected). It returns a new contract instance.

let p = Promise.reject();
let fun = function () {
  console.log('1');
}
p.then(null, fun);
p.catch(fun);
Copy the code

Promise.prototyep.finally():

Promise. Prototyep. Finally add () method is used to issue about onFinally handler, the handler in the futures will perform when converted to solve or reject status, this method can solve handlers and refused to avoid redundant code handler, But it doesn’t know whether the date status is resolved or rejected, so this method is mostly used to add cleanup code. This instance of the new contract, unlike those returned by then and catch, is designed to be a state-independent method, so most of the time it will be passed on as it is, whether the parent contract is resolved or rejected.

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('bar'));
// All of these will be Promise
      
       : foo
      

// Note: If a pending term is returned, or if the onFinally handler throws an error (indicating that a rejected term was thrown or returned), a corresponding period (pending or rejected) is returned.

let p9 = p1.finally(() = > new Promise(() = > {}));
let p10 = p1.finally(() = > Promise.reject());
let p11 = p1.finally(() = > { throw 'bar' });

console.log(p9); // Promise <pending>
console.log(p10); // Promise <rejected>: undefined
console.log(p11); // Promise <rejected>: bar
Copy the code

Pass resolution values and rejection reasons

When the current contract reaches the settled state, the contract provides its settlement value or reason for rejection to the relevant state handler. Once you get the return value from the previous step, you can further process the value. For example, if the data returned by the first network request is the parameter value of the second request, the value returned by the first request should be passed to the resolution handler for further processing. Failed network requests need to be passed to a rejection handler. Note: Values passed when a contract is resolved can only be accepted by the resolution handler, and values passed when a contract is rejected can only be accepted by the rejection handler. Multiple resolution handlers need only be followed by a rejection handler. The diagram below:

Reject terms and reject error handling

Rejection terms are similar to throw() expressions in that they represent a program state that requires an interrupt or special processing. Throwing an error in the execution function or handler of a contract will result in rejection, and the corresponding error object will be the reason for 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')); All four of these conditions can lead to an offer turning into a rejection,Promise <rejected>: Error: foo
Copy the code

Note: Dates can be rejected for any reason, including undefined, but it is best to use error objects in general, because creating error objects allows the browser to capture stack trace information in the object, which is critical for debugging. When throwing an error using a throw, the js runtime error handling mechanism stops any instruction following the throw. However, when you use reject to handle errors in a contract, the error is thrown asynchronously and does not prevent the execution of other code.

Promise.all()

This method accepts a set of terms, and the terms created by this method are resolved only if all terms are resolved. This static method takes an iterable and 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([]);
// Invalid syntax
let p4 = Promise.all();

// If all terms are successfully resolved, the solution values of the synthesized term are an array containing the solution values of the term, in the order they appear in the iterator.
let p = Promise.all([
  Promise.resolve(1),
  Promise.resolve(),
  Promise.resolve(3),
])
p.then(values= > {
  console.log(values); // [1, undefined, 3]
})

// If a term contract is rejected, the first term that rejects it will use its own reasons as the reason for rejecting the combined term contract, and the subsequent term that rejects it will not
// Affects the final contract rejection grounds, but this does not affect the normal rejection operations of all contained terms, synthetic
// Date rejection silently handles all rejection operations that contain dates, with no unresolved errors thrown.
Copy the code

Promise.race()

This method returns a wrapper term, a mirror image of the first resolved or rejected term in a set of collections, accepts an iterable and returns a new term.

let p1 = Promise.race([
  Promise.resolve(),
  Promise.resolve(),
])
setTimeout(() = > {
  console.log(p1); // Promise <resolved> : undefined
}, 0);

// Elements in an iterable are converted to a contract by promise.resolve ()
let p2 = Promise.race[3.4];
setTimeout(() = > {
  console.log(p2); // Promise <resolved> : 3
}, 0);

// Empty iterables are equivalent to new Promise(() => {})
let p3 = Promise.race([]);

// Invalid syntax
let p4 = Promise.race();
Copy the code

Note: the promise. race method does not discriminate between resolved and rejected terms. As long as it is the first settled term, a Promise wraps its settlement value or rejection reason and returns the new term. If there is a term rejection and it is the first one settled, it becomes a rejection of the promise.race () return term. Subsequent terms rejected do not affect the rejection of the final term. However, this does not affect the normal rejection of all terms, similar to promise.all (), Promise.race() also silently handles all operations that contain contract rejections without throwing uncaught errors.