In fact, before the emergence of ES6 standard, the community first proposed the Promise scheme, and then with the inclusion of ES6, it unified its usage and provided the native Promise object.

The basics of promises

If I had to explain what a Promise is, it’s simply a container that holds the result of some event (usually an asynchronous operation) that will end in the future. Syntactically, a Promise is an object from which to get messages for asynchronous operations.

Promise provides a uniform API, and all kinds of asynchronous operations can be handled in the same way. Let’s take a quick look at the chained invocation code for the Promise implementation, as shown below.

function read(url) {
    return new Promise((resolve, reject) = > {
        fs.readFile(url, 'utf8'.(err, data) = > {
            if(err) reject(err);
            resolve(data);
        });
    });
}

read(A).then(data= > {
    return read(B);
}).then(data= > {
    return read(C);
}).then(data= > {
    return read(D);
}).catch(reason= > {
    console.log(reason);
});
Copy the code

Promise objects are created in an undetermined state, which allows you to associate an asynchronous operation that returns a final success value or failure reason with the appropriate handler.

If the contents are helpful to you, please use your hands to help small make up: click on this link to add a chicken leg: github.crmeb.net/u/xingfu

In general, a Promise must be in one of the following states during its execution.

  1. Pending: An initial state that has not been completed or rejected.
  2. This is a big pity: The operation is completed successfully.
  3. Rejected: The operation fails.

A Promise object in a pending state, executed, will either be completed with a value or rejected for a reason. When one of these situations occurs, our associated handlers, arrayed with the Promise’s then methods, are called. Because the final promise.prototype. then and promise.prototype. catch methods return a Promise, they can continue to be called chained.

One thing to note about Promise state transitions is that internal state changes are irreversible, and you need to be aware of that during programming. The text description is obscure, so we can see the internal state flow of Promise clearly through a picture, as shown below (the picture comes from the Internet).

How does Promise solve callback hell

First, recall what callback hell is. There are two main problems with callback hell:

  1. Multi-layer nesting problem;
  2. There are two possibilities (success or failure) for each task. You need to handle the two possibilities respectively after each task is executed.

Promise was created to address both of these problems, especially in the era of callback functions. Promise uses three techniques to solve callback hell: callback function delay binding, return value penetration, and error bubbling.

Let’s illustrate this with a piece of code, as shown below.

let readFilePromise = filename= > {
  return new Promise((resolve, reject) = > {
    fs.readFile(filename, (err, data) = > {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

readFilePromise('1.json').then(data= > {
  return readFilePromise('2.json')});Copy the code

As you can see from the code above, the callback function is not declared directly, but is passed in via the later THEN method, which is called deferred binding. Let’s make a few tweaks to the code above, as shown below.

let x = readFilePromise('1.json').then(data= > {
  return readFilePromise('2.json')  // This is the return Promise
});
x.then(/* Internal logic omits */)
Copy the code

We create different types of promises based on the incoming value of the callback function in THEN, and then pass the returned Promise through the outer layer for subsequent calls. The x here refers to the internally returned Promise, which can then be followed by the chain call. This is the effect of return value penetration, and the two techniques work together to write deep nested callbacks in the following form.

If the contents are helpful to you, please use your hands to help small make up: click on this link to add a chicken leg: github.crmeb.net/u/xingfu

readFilePromise('1.json').then(data= > {
    return readFilePromise('2.json');
}).then(data= > {
    return readFilePromise('3.json');
}).then(data= > {
    return readFilePromise('4.json');
});
Copy the code

This is a lot cleaner, more importantly, it is more in line with the human thinking model, the development experience is better, the two technologies combined to produce the effect of chain call.

This solves the problem of multiple layers of nesting, but what about the other problem of handling success and failure separately at the end of each task? Promise took the error bubble approach. It’s actually pretty easy to understand. Let’s see what happens.

readFilePromise('1.json').then(data= > {
    return readFilePromise('2.json');
}).then(data= > {
    return readFilePromise('3.json');
}).then(data= > {
    return readFilePromise('4.json');
}).catch(err= > {
  // xxx
})
Copy the code

This way errors are passed backwards and caught, so you don’t have to check for errors as often. As can be seen from the above code, the Promise solution effect is also quite obvious: to achieve the chain call, solve the problem of multi-layer nesting; One-stop processing after error bubbling is realized to solve the problem of incorrect judgment and increased code confusion in each task.

Let’s take a look at what static methods Promise provides.

A static method for Promise

I will introduce the four methods, All, allSettled, Any and RACE, in terms of syntax, parameters and method code.

All methods

  • Syntax: Promise. All (iterable)
  • Argument: an iterable, such as Array.

Description: This method is useful for summarizing the results of multiple promises. In ES6, multiple promise.all asynchronous requests can be processed in parallel.

  1. Return success in the requested order when all results are returned successfully.
  2. A failed method is entered when one of them fails.

Let’s take a look at the business scenario. For the following business scenario page load, it might be better to merge multiple requests together using all, as shown in the code snippet.

//1. Obtain the list of multicast data

function getBannerList(){
  return new Promise((resolve,reject) = >{
      setTimeout(function(){
        resolve('Round data')},300)})}//2. Get the store list

function getStoreList(){
  return new Promise((resolve,reject) = >{
    setTimeout(function(){
      resolve('Store Data')},500)})}//3. Get the category list

function getCategoryList(){
  return new Promise((resolve,reject) = >{
    setTimeout(function(){
      resolve('Categorical data')},700)})}function initLoad(){ 
  Promise.all([getBannerList(),getStoreList(),getCategoryList()])
  .then(res= >{
    console.log(res) 
  }).catch(err= >{
    console.log(err)
  })
} 

initLoad()
Copy the code

If the contents are helpful to you, please use your hands to help small make up: click on this link to add a chicken leg: github.crmeb.net/u/xingfu

As can be seen from the above code, the three operations of obtaining the rotation list, obtaining the store list and obtaining the classification list need to be loaded in a page, and the page needs to send requests for page rendering at the same time, which can be realized by using promise.all to make it more clear and obvious.

Now let’s look at another method.

AllSettled method

Promise.allsettled has similar syntax and parameters to promise.all, which accepts an array of promises and returns a new Promise. The only difference is that there is no failure after execution, which means that when promise.allSettled is finished, we can get the state of each Promise, regardless of whether it was processed successfully or not.

Let’s look at a piece of code implemented with allSettled.

const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
  console.log(results);
});

// Return result:
/ / /
// { status: 'fulfilled', value: 2 },
// { status: 'rejected', reason: -1 }
// ]
Copy the code

As you can see from the code above, promise.allSettled finally returns an array of the return values of each Promise in the parameters passed in, which is not the same as the all method. You can also adapt the code for the business scenario provided by the All method to know that after multiple requests have been made, the Promise will eventually return the final state of each parameter.

Now let’s look at the method any.

Any way

  • Syntax: Promise. Any (iterable)
  • Parameters: iterable An iterable object, such as Array.

The any method returns a Promise. As long as one of the parameter Promise instances becomes a fulfilled state, the final instance returned by any will become a fulfilled state. If all parameter Promise instances become the Rejected state, the wrapper instance becomes the Rejected state.

After modifying the code allSettled above, let’s look at the modified code and the execution result.

const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const anyPromise = Promise.any([resolved, rejected]);
anyPromise.then(function (results) {
  console.log(results);
});

// Return result:
/ / 2
Copy the code

As you can see from the modified code, if one of the promises becomes a pity state, then any will finally return this Promise. Resolved Promise (resolve)

Finally, let’s look at the Race method.

Race method

  • Syntax: promise.race (iterable)
  • Parameters: iterable An iterable object, such as Array.

The RACE method returns a Promise, and the return state of the race method changes as long as one of the parameters’ Promise instances changes state first. The return value of the first changed Promise instance is passed to the race method’s callback function.

Let’s take a look at this business scenario. For image loading, race method is particularly suitable for solving the problem. The image request and timeout judgment are put together, and race is used to realize the image timeout judgment. Look at the code snippet.

// Request an image resource

function requestImg(){
  var p = new Promise(function(resolve, reject){
    var img = new Image();
    img.onload = function(){ resolve(img); }
    img.src = 'http://www.baidu.com/img/flexible/logo/pc/result.png';
  });

  return p;
}

// The delay function is used to time the request

function timeout(){
  var p = new Promise(function(resolve, reject){
    setTimeout(function(){ reject('Image request timed out'); }, 5000);
  });

  return p;
}

Promise.race([requestImg(), timeout()])
.then(function(results){
  console.log(results);
})

.catch(function(reason){
  console.log(reason);
});
Copy the code

If the contents are helpful to you, please use your hands to help small make up: click on this link to add a chicken leg: github.crmeb.net/u/xingfu

As you can see from the code above, using the Promise approach to determine whether images load successfully is also a good business scenario for the promise.race approach.

To sum up, the parameter transfer form of these four methods is basically the same, but the function of each method is slightly different in the end, which you need to pay attention to.

conclusion

Okay, so that’s it for this lecture. I’ve finally put together a few Promise methods that you can review again in the chart below.

If the contents are helpful to you, please use your hands to help small make up: click on this link to add a chicken leg: github.crmeb.net/u/xingfu