Quickly learn about Promise of ES6

Keywords: Javascript Front-end ECMAScript Programmer

ECMAScript 6 adds perfect support for Promises/A + specification, namely Promise type. Once launched, Promise became very popular and became the dominant asynchronous programming mechanism. All modern browsers support ES6, and many other browser API s are based on ES6.

Promise is a new reference type in ECMAScript 6, which indicates the final completion or failure of an asynchronous operation. Promise creates objects with the following syntax:

let promise = new Promise(function(resolve, reject) => {
    // Asynchronous processing
    //   resolve(value)              // fulfilled
    // or
    //   reject(error)    // rejected
});

From the above, Promise is a stateful object. The returned Promise object may be in one of the following states:

  • pending: initial status, i.e. not cashed or rejected.
  • fulfilled, sometimes referred to as "resolved": it means that the operation is successfully completed.
  • rejected: means the operation failed.

Pending is the initial state of Promise. In the pending state, Promise can be set to represent the successful cashed state or the failed rejected state. No matter which state is settled, it is irreversible. As long as it changes from pending to cash or reject, Promise's status will not change. Moreover, there is no guarantee that Promise will inevitably leave the pending state. Therefore, well-organized code, whether Promise resolve or reject, or even always in the pending state, should have appropriate behavior.

Importantly, Promise's state is private and cannot be detected directly through JavaScript. This is mainly to avoid processing Promise instances synchronously according to the read Promise status. In addition, Promise's state cannot be modified by external JavaScript code. This is the same reason that the state cannot be read: Promise deliberately encapsulates asynchronous behavior to isolate external synchronous code.

Promise instance method

Promise instance is a bridge between external synchronous code and internal asynchronous code. These methods can access the data returned by asynchronous operations, process the success and failure results of promise objects, continuously evaluate promise, or add code that will be executed only when promise enters the termination state.

Promise.prototype.then()

The Promise.prototype.then() method returns a Promise instance, which is the main method for adding processing to the Promise instance.

Promise.prototype.then(onFulfilled, onRejected)
  • On fulfilled: optional parameter. Function called when Promise state changes to full. Any non function types passed in are silently ignored.
  • onRejected: optional parameter. The function called when the Promise state changes to rejected. Any non function types passed in are silently ignored.

When the value provides the onRejected parameter, you need to upload undefined in the onFulfilled parameter position. It helps to avoid creating redundant objects in memory and is also an explanation for the type system of expected function parameters.

Promise.prototype.catch()

The Promise.prototype.catch() method returns a Promise instance and handles the rejection.

Promise.prototype.catch(onRejected)
  • onRejected: a Function called when Promise is rejected. This Function has a parameter reason, which indicates the reason for rejection.

If onRejected throws an error or returns a failed Promise, the Promise returned through catch() is rejected; Otherwise, it will display as resolved. This method can be used for error handling in Promise combination. In fact, this method is a syntax sugar. Calling it is equivalent to Promise.prototype.then(null, onRejected).

Promise.prototype.finally()

The Promise.prototype.finally() method returns a Promise instance, and when the Promise processing ends, the specified callback function will be executed regardless of the state. This provides a way for code to be executed after Promise is successfully completed. Redundant code in then() and catch() is also avoided.

Promise.prototype.finally(onFinally)
  • onFinally: when the Promise is received, the Function is called.

However, the state of Promise cannot be known in the onfinanally function above, so this method is mainly used to add cleaning code.

error handling

The Promise with the status of rejected is similar to the throw() expression. Both represent a program state, that is, it needs interrupt or special processing. Errors thrown during Promise execution will result in rejected, and the corresponding Error will become the reason for rejection.

let p = new Promise((resolve, reject) => reject(throw Error('Error Value')));

An error thrown in the Promise instance will be asynchronously thrown from the message queue and will not prevent the runtime from continuing to execute the synchronization instruction:

Promise.reject(Error('Error Value'));
console.log('initial value');
// initial value
// Uncaught (in promise) Error: Error Value

Therefore, promise can only catch errors through the catch instance method, and the try catch statement block is not easy to use. Promise's then and catch methods are equivalent to try catch.

Promise static method

There are multiple static methods in Promise reference type. Except Promise.reject() and Promise.resolve(), they will receive a parameter of Promise's iterable type. The synthesized Promise behavior depends on the behavior of the internal Promise.

Note: Promise.any() method is still experimental and not fully supported.

Promise.reject()

Promise.reject() returns a promise instance with the status of rejected and throws an asynchronous error.

Promise.reject(reason);
  • reason: indicates the failure information of Promise instance.

The use of Promise.reject() is actually the same as that of new Promise ((resolve, reject) = > reject()). When the Error reason is passed in, an Error instance can be passed in to make it the Error reason of the returned Promise instance:

Promise.reject(new Error("Promise instance failed")).then(function() {
    // not called
}, function(error) {
    console.error(error);    // Stacktrace
});
setTimeout(console.log, 0, Promise.reject(Promise.resolve()));
// Promise <rejected>: Promise <resolved>

Promise.resolve()

Through Promise.resolve() static method, you can instantiate a Promise instance with the status of fully. It is actually the same as new Promise ((resolve, reject) = > resolve()).

Promise.resolve(value)
  • value: the parameter to be resolved by the Promise instance object. It can also be a Promise instance or a thenable.

Using this static method, you can convert any value to an instance of Promise whose state is full, and the extra values passed in will be ignored.

setTimeout(console.log, 0, Promise.resolve());
// Promise <resolved>: undefined
setTimeout(console.log, 0, Promise.resolve(4, 5, 6));
// Promise <resolved>: 4

When the passed in parameter itself is a Promise instance, the behavior is similar to an empty wrapper. Therefore, Promise.resolve() can be said to be an idempotent method.

let p = Promise.resolve(7);
setTimeout(console.log, 0, p === Promise.resolve(p));
// true
setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p)));
// true

This idempotency preserves the state of the incoming period:

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

However, it can wrap any non Promise value, including the error object, and turn it into a resolved Promise instance, which will lead to unexpected behavior. Note:

let p = Promise.resolve(new Error('foo'));
setTimeout(console.log, 0, p);
// Promise <resolved>: Error: foo

Promise.all()

The Promise.all() method receives a collection of iterable types with Promise instances and returns a new Promise instance.

When the state of all Promise instances changes to full, the state of the returned Promise instances changes to full, and the solution value is all arrays containing the solution values of Promise instances, in the order of iterators.

let p = Promise.all([
    Promise.resolve(3),
    Promise.resolve(),
    Promise.resolve(4)
]);
p.then((values) => setTimeout(console.log, 0, values)); // [3, undefined, 4]

When the state of a Promise instance changes to rejected, the state of the returned Promise instance changes to rejected, and the returned rejection reason is the rejection reason of the Promise instance whose state changes to rejected. The rejection of subsequent Promise instances will not affect the rejection reason of the final Promise. However, this does not affect all normal reject operations that contain Promise instances. The Promise instance returned by Promise.all will silently handle all rejection operations containing Promise instances, as shown below:

// Although only the reasons for refusal of the first term will enter
// Rejection handler, rejection of the second appointment
// Will be handled silently, and 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

Promise.allSettled()

ECMAScript 2020 adds Promise.allSettled() method. In the iterable collection received by the Promise.allSettled() method, the status of the Promise instance is either fully or rejected.

Promise.allSettled([
    Promise.resolve(3),
    new Promise((resolve, reject) => {
        setTimeout(reject, 1000);
    }),
    Promise.reject('reject promise')
]).then((results) => console.log(results));

/**
 * Output results
 * [
 *   { status: 'fulfilled', value: 3 },
 *   { status: 'rejected', reason: undefined },
 *   { status: 'rejected', reason: 'reject promise' }
 * ]
 */

The Promise.allSettled() method is useful when you want to ensure that all Promise instances end.

Promise.race()

In the iterable set received by the Promise.race() method, as long as the state of one Promise instance changes to fully or rejected, it will wrap its solution value or rejection reason and return a new Promise instance.

// The resolution occurs first, and the 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 <resolved>: 3
// Rejection occurs first, and the 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

Similar to Promise.all(), the rejection reason in the rejected Promise instance will become the rejection reason of Promise.race(). The remaining Promise instances in the iterable set will be processed silently without errors.

// Although only the rejection reasons of the first contract will enter the rejection processing program, the rejection of the second contract will also be processed silently without errors
let p = Promise.race([
    Promise.reject(3),
    new Promise((resolve, reject) => setTimeout(reject, 1000))
]);
p.catch((reason) => setTimeout(console.log, 0, reason)); // 3
// There are no unhandled errors

call chaining

The then(), catch() and finally() methods provided by Promise will return a new Promise instance. The new Promise instance can call these methods, and the method calls can be concatenated to form a chain call. As follows:

let p = new Promise((resolve, reject) => {
    console.log('initial promise');
    reject();
}).catch(() => {
    console.log('reject handler');
}).then(() => {
    console.log('resolve handler');
}).finally(() => {
    console.log('finally handler');
});

/**
 * Output results
 * initial promise
 * reject handler
 * resolve handler
 * finally handler
 */

Chain calls will be executed in sequence. Each Promise will wait for the previous Promise to solve, and then instantiate a new Promise instance to return it. This structure can succinctly serialize asynchronous tasks.

let p = new Promise((resolve, reject) => {
    console.log('initial promise');
    setTimeout(resolve, 1000);
}).then(() => new Promise((resolve, reject) => {
    console.log('first promise');
    setTimeout(resolve, 3000);
})).then(() => new Promise((resolve, reject) => {
    console.log("second promise");
    setTimeout(resolve, 2000);
})).then(() => new Promise((resolve, reject) => {
    console.log("third promise");
    setTimeout(resolve, 1000);
}));
/**
 * Output results
 * initial promise
 * first promise
 * second promise
 * third promise
 */

More content, please pay attention to the official account of the sea.

Posted by --ss-- on Thu, 18 Nov 2021 05:04:08 -0800