ECMAscript 6 natively provides Promise objects.

The Promise object represents an event that will happen in the future and is used to deliver messages for asynchronous operations.

Promise objects have the following two characteristics:

1. The state of the object is not affected by the outside world. The Promise object represents an asynchronous operation and has three states:

  • pending: the initial state, not a success or failure state.

  • fulfilled: means the operation completed successfully.

  • rejected: means the operation failed.

Only the result of the asynchronous operation can determine the current state, and no other operation can change this state. This is also the origin of the name Promise, which means "promise" in English, which means that it cannot be changed by other means.

2. Once the state changes, it will not change again, and this result can be obtained at any time. There are only two possibilities for the state of the Promise object to change: from Pending to Resolved and from Pending to Rejected. As long as these two situations occur, the state will be frozen, will not change anymore, and will always maintain this result. Even if the change has occurred, if you add a callback function to the Promise object, you will get this result immediately. This is completely different from an event. The feature of an event is that if you miss it, you can listen to it and you will get no results.

Promises pros and cons

With the Promise object, asynchronous operations can be expressed in a synchronous operation process, avoiding layers of nested callback functions. In addition, the Promise object provides a unified interface, making it easier to control asynchronous operations.

Promises also have some disadvantages. First of all, the Promise cannot be cancelled. Once it is created, it will be executed immediately and cannot be cancelled halfway. Secondly, if the callback function is not set, the error thrown inside the Promise will not be reflected to the outside. Third, when it is in the Pending state, it is impossible to know which stage it is currently in (just started or is about to be completed).

Promise creation

To create a promise object, you can use new to call the Promise constructor to instantiate it.

Here are the steps to create a promise:

var promise = new Promise(function(resolve, reject) {
     // Asynchronous processing
     // After processing, call resolve or reject
});

The Promise constructor contains one parameter and a callback with two parameters resolve and reject. Perform some operations in the callback (such as asynchronous), if everything is normal, call resolve, otherwise call reject.

Example

var myFirstPromise = new Promise(function(resolve, reject){
     //When the asynchronous code executes successfully, we will call resolve(...), when the asynchronous code fails, we will call reject(...)
     //In this example, we use setTimeout(...) to simulate asynchronous code. The actual coding may be XHR requests or some HTML5 API methods.
     setTimeout(function(){
         resolve("Success!"); //The code is executed normally!
     }, 250);
});
 
myFirstPromise.then(function(successMessage){
     //The value of successMessage is the value passed in by calling the resolve(...) method above.
     //successMessage parameter does not have to be a string type, here is just an example
     document.write("Yay! "+ successMessage);
});

 Try It! 

The promise.then() method can be called for the promise object that has been instantiated, passing the resolve and reject methods as callbacks.

promise.then() is the most commonly used method for promises.

promise.then(onFulfilled, onRejected)

Promise simplifies the handling of errors. The above code can also be written like this:

promise.then(onFulfilled).catch(onRejected)

Promise Ajax

The following is an example of an Ajax operation implemented with a Promise object.

Example

function ajax(URL) {
     return new Promise(function (resolve, reject) {
         var req = new XMLHttpRequest();
         req.open('GET', URL, true);
         req.onload = function () {
         if (req.status === 200) {
                 resolve(req.responseText);
             } else {
                 reject(new Error(req.statusText));
             }
         };
         req.onerror = function () {
             reject(new Error(req.statusText));
         };
         req.send();
     });
}
var URL = "/try/ajax/testpromise";
ajax(URL).then(function onFulfilled(value){
     document.write('The content is:' + value);
}).catch(function onRejected(error){
     document.write('error:' + error);
});

In the above code, both the resolve method and the reject method are called with parameters. Their parameters will be passed to the callback function. The parameter of the reject method is usually an instance of the Error object, and the parameter of the resolve method may be another Promise instance in addition to the normal value, such as the following.

var p1 = new Promise(function(resolve, reject){
   // ... some code
});
 
var p2 = new Promise(function(resolve, reject){
   // ... some code
   resolve(p1);
})

In the above code, p1 and p2 are both Promise instances, but the resolve method of p2 takes p1 as a parameter, and then the state of p1 will be passed to p2. If the state of p1 is pending when called, the callback function of p2 will wait for the state of p1 to change; if the state of p1 is already fulfilled or rejected, the callback function of p2 will be executed immediately.


Promise.prototype.then method: chain operation

The Promise.prototype.then method returns a new Promise object, so it can be written in chain.

getJSON("/posts.json").then(function(json) {
   return json.post;
}).then(function(post) {
   // proceed
});

The above code uses the then method to specify two callback functions in turn. After the first callback function is completed, the return result will be used as a parameter and passed to the second callback function.

If the previous callback function returns a Promise object, then the next callback function will wait for the Promise object to have a running result before calling it further.

getJSON("/post/1.json").then(function(post) {
   return getJSON(post.commentURL);
}).then(function(comments) {
   // Process the comments
});

This design allows nested asynchronous operations to be easily rewritten, from the "horizontal development" of the callback function to the "downward development".


Promise.prototype.catch method: catch errors

The Promise.prototype.catch method is an alias of Promise.prototype.then(null, rejection), which is used to specify the callback function when an error occurs.

getJSON("/posts.json").then(function(posts) {
   // some code
}).catch(function(error) {
   // Handle the error that occurred while the previous callback function was running
   console.log('An error occurred!', error);
});

The error of the Promise object is "bubbling" in nature and will be passed backwards until it is caught. In other words, the error will always be caught by the next catch statement.

getJSON("/post/1.json").then(function(post) {
   return getJSON(post.commentURL);
}).then(function(comments) {
   // some code
}).catch(function(error) {
   // Handle the errors of the first two callback functions
});

Promise.all method, Promise.race method

The Promise.all method is used to wrap multiple Promise instances into a new Promise instance.

var p = Promise . all ([ p1 , p2 , p3 ]); 

In the above code, the Promise.all method accepts an array as a parameter, and p1, p2, and p3 are all instances of Promise objects. (The parameter of the Promise.all method is not necessarily an array, but it must have an iterator interface, and each member returned is a Promise instance.)

The state of p is determined by p1, p2, and p3, which can be divided into two cases.

  • (1) Only when the states of p1, p2, and p3 all become fulfilled, will the state of p become fulfilled. At this time, the return values of p1, p2, and p3 form an array, which is passed to the callback function of p.

  • (2) As long as one of p1, p2, and p3 is rejected, the status of p becomes rejected. At this time, the return value of the first rejected instance will be passed to the callback function of p.

The following is a specific example.

// Generate an array of Promise objects
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
   return getJSON("/post/" + id + ".json");
});
 
Promise.all(promises).then(function(posts) {
   // ...
}).catch(function(reason){
   // ...
});

The Promise.race method also wraps multiple Promise instances into a new Promise instance.

var p = Promise.race([p1,p2,p3]);

In the above code, as long as one instance of p1, p2, and p3 changes state first, the state of p will change accordingly. The return value of the Promise instance that changes first is passed to the return value of p.

If the parameters of the Promise.all method and Promise.race method are not Promise instances, the Promise.resolve method described below will be called first to convert the parameters to Promise instances, and then further processing.


Promise.resolve method, Promise.reject method

Sometimes it is necessary to convert an existing object into a Promise object, and the Promise.resolve method plays this role.

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

The above code turns jQuery into a deferred object and converts it into a new ES6 Promise object.

If the parameter of the Promise.resolve method is not an object with a then method (also known as a thenable object), a new Promise object is returned, and its status is fulfilled.

var p = Promise.resolve('Hello');
 
p.then(function (s){
   console.log(s)
});
// Hello

The above code generates an instance p of a new Promise object, its status is fulfilled, so the callback function will be executed immediately, and the parameters of the Promise.resolve method are the parameters of the callback function.

If the parameter of the Promise.resolve method is an instance of a Promise object, it will be returned intact.

The Promise.reject(reason) method will also return a new Promise instance whose status is rejected. The parameter reason of the Promise.reject method will be passed to the callback function of the instance.

var p = Promise.reject('Something went wrong');
 
p.then(null, function (s){
   console.log(s)
});
// error

The above code generates an instance of the Promise object, the state is rejected, and the callback function will be executed immediately.

Reference link:

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise