Some time ago when I was looking for a job, I interviewed more than a dozen companies, large and small, including Tencent, Blue and other well-known companies. Summary interview experience, found that they still have a lot of shortcomings, many knowledge points are not know why. Taking advantage of the recent fewer things, will gradually summarize some high-frequency knowledge points of the interview, improve the depth and breadth of their knowledge. This article is part of a series: Understanding promises.

What is Promise, and why do we use it?

Due to the nature of the JavaScript language, all programs are single-threaded. Because of this feature, some of JavaScript’s browser events, request events, are executed asynchronously, with the result of the async callback. This is a common syntax, but in some cases, it creates a “callback hell” of nested callback functions, or even multiple layers of callback functions, which makes code unreadable and difficult to maintain.

<! An example of a hell callback, where the result of the previous function's callback is dependent on the result of the next function.const verifyUser = function(username, password, callback){
   require.verifyUser(username, password, (error, userInfo) => {
       if (error) {
           callback(error)
       }else{
           require.getRoles(userInfo, (error, roles) => {
               if (error){
                   callback(error)
               }else {
                   require.logAccess(roles, (error) => {
                       if (error){
                           callback(error);
                       }else{
                           callback(null, userInfo, roles); }})}})})}Copy the code

In order to solve this problem, the community came up with a number of solutions to solve asynchronous callbacks using the chain call method, which was standardized in ES6. Promise is arguably a solution to asynchronous programming.

Basic use of Promise

Basic usage

As a new specification, Promise addresses callback nesting in a more intuitive and readable way. ES6 states that the Promise object is a constructor that generates an instance through the new keyword. Here are the basic uses of promise

<! -- Basic use of promise -->const promise = new Promise((resolve, reject) = > {
  // Code for asynchronous operations
  if (success){
    resolve(value);
  } else{ reject(error); }});Copy the code

The Promise constructor takes as an argument a function called resolve and reject, two functions provided by the JavaScript engine. Asynchronous operations have two outcomes: success or failure

  • Resolve is triggered when an asynchronous operation changes from the pending state to the resolved state and passes the result of the operation.
  • The Reject function is fired when the asynchronous operation changes from the Pending state to the Rejected state, and passes the result of the failed operation.

Note that promsie status can only be modified by pending => depressing/Rejected

Promise object methods

Promise.prototype.then()

So the question is, where do we write the code to handle state changes when we say that the promise state changes and then triggers the corresponding function? Yes, the then() method. The then method is defined on the prototype promise. prototype and adds a state-changing callback to the Promise instance. The first argument to the THEN method is the Resolved state callback and the second argument (optional) is the Rejected state callback.

<! -- Promise then method -->const promise = new Promise((resolve, reject) = > {
   resolve('fulfilled'); // State by pending => depressing
 }).then(result= > { 
    console.log(result); // 'fulfilled' 
 }, reason => { 
    
 })
Copy the code
const promise = new Promise((resolve, reject) = > {
  reject('rejected '); // The status is changed from Pending => Rejected
}).then(result= > { 
    
}, reason => { 
   console.log(reason); // 'rejected'
})
Copy the code

Once the promise state is modified, it cannot be changed until pending => fulfilled or pending => Fulfilled

Then () registers a callback function for a promise that returns the result of the previous task, so the function in then must return a result or a new promise object for subsequent THEN callbacks to receive.

 const promise = new Promise((resolve, reject) = > {
     resolve("success")
 })
.then(result= > {
    return result
})
.then(result= > {
    console.log(result)  // "success"
})
.catch(err= > {})
Copy the code

Promise.prototype.catch()

Promise.prototype. Catch is a. Then (null, rejection) and also a. Then (undefined, rejection) callback when an asynchronous operation fails. Errors in the then() callback are also caught by catch().

<! --promisecatchMethods -- - >const promise = new Promise((resolve, reject) = > {
   throw new Error('err');
   // reject(new Error('err'));
 }).then(result= > { 
    console.log(result); 
 }).catch(err= > {
    // Handle errors from the first two promises
     console.log(err)
 })
Copy the code

At this point, careful students will see why I use the catch() method when the second argument to my then() method can be used to throw an error. There’s a difference. In chained operations, any synchronous or asynchronous errors thrown by a promise are caught by then(), while reject handles the current promise’s errors. Therefore, it is advisable not to define a reject callback (the second argument to then) in the THEN method, but to always use a catch method, which is closer to synchronous writing (try/catch).

<! --promisecatchMethods -- - >const promise = new Promise((resolve, reject) = > { 
    // some code
})
// good
promise.then(result= > { 
   // success
}).catch(err= > {
    // err
})
// not recommend
promise.then(result= > {
    //success
},err => {
    //err
});
Copy the code

Promise.prototype.finally()

The finally() method, introduced as a standard in ES2018, means that no matter what state a promise is in, it will finally execute a finally() method after executing either the THEN () or catch() methods.

<! -- promisefinallyMethods -- - >const promise = new Promise((resolve, reject) = > {})
.then(result= > {})
.catch(err= > {})
.finally((a)= > {})
Copy the code

Promise.all()

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

<! -- promise.all method -->const promise1 = new Promise((resolve, reject) = > {resolve("promise1")}),
     promise2 = new Promise((resolve, reject) = > {resolve("promise2")}),
     promise3 = new Promise((resolve, reject) = > {resolve("promise3")});
     
Promise.all([promise1,promise2,promise3]).then(data= > { 
  console.log(data); 
  // ["promise1", "promise2", "promise3"] results are in the same order as the array of promise instances
}).catch(err= > {
  consolo.log(err)
});
Copy the code

The state of Promise. All will become a big pity. Then the return values of promise1, promise2, and promise3 form an array. The callback function passed to promise.all.

Promise.all becomes rejected when one of promise1, promise2, or promise3 is rejected. The return value of the first rejected instance is passed to the promise. all callback function

When doing projects, we often encounter multiple requests for a page. We can use promise.all to encapsulate requests for easy management.

Similarly, axios has the axios.all() method to handle concurrent requests

Promise.race()

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

<! -- promise.race method -->const promise = Promise.race([promise1, promise2, promise3]);
Copy the code

In this code, the state of a promise changes as soon as one instance of promisE1, promise2, or promise3 changes state first. The return value of the first changed Promise instance is passed to p’s callback.

Iii. Application of Promise

Combined with axios

In the project, we often encounter situations where we need to rewrap AXIos based on business, such as request interception setting token and Content-Type, and response interception setting different responses based on different status codes. We can also rewrap axios

import axios from "./axios"
import qs from "qs";
export default {
    get: function(url, params) {
      return new Promise((resolve, reject) = > {
        axios.get(url, {params: params})
          .then(res= > {
            resolve(res)
          })
          .catch(err= > {
           console.log(err)
          })
      })
    },
    post: function(url, params) {
      return new Promise((resolve, reject) = > {
        axios.post(url, qs.stringify(params))
          .then(res= > {
            resolve(res);
          })
          .catch(err= > {
              console.log(err) }) }); }} <! Requests using the entire user module are managed in this file -->import require from "@/utils/require"
const user = {
    userList() {
      return require.post("/api.php", {}).then(res= > res.result)
    },
    userInfo() {
      return require.post("/api.php? &uid=20", {}).then(res= > res.result)
    },
    ...
}
export default user
Copy the code

Asynchronously loading images

An example of asynchronously loading images with Promise

function loadImageAsync(url) {
    return new Promise((resolve, reject) = > {
      const image = new Image();
      image.onload = (a)= > {
        resolve(image);
      };
      image.onerror = (a)= > {
        reject(new Error('Could not load image at ' + url));
      };
      image.src = url;
    });
}
const loadImage = loadImageAsync(url);
Copy the code

Write in the last

Front-end technology has developed rapidly in recent years, and new technologies emerge in endlessly. As a programmer, ER should also have the consciousness of continuous learning. Welcome to pay attention to my public number: front-end Readhub. 😄 😄