Promise Basics

1. What is a promise and why should we use it?

Due to the JavaScript language features, all programs are executed in a single thread. Due to this feature, some browser events and request events of JavaScript are executed asynchronously, and the asynchronous results are processed through callback functions. This is a very common syntax, but in some scenarios, callback functions will be formed to nest callback functions, and in some cases, multiple layers will be applied, forming "callback hell", which makes the code bloated and poorly readable and difficult to maintain.

<!--An example of a callback from hell where the result of the previous function's callback is depended on by 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 code

In order to solve this problem, the community has proposed some solutions, using the method of chaining calls to solve asynchronous callbacks, and has been unified into a specification in ES6. It can be said that Promise s are a solution to asynchronous programming.

Second, the basic usage of Promise

Basic usage

As a new specification, promise s solve callback nesting in a more intuitive and readable way. ES6 stipulates that the promise object is a constructor that generates an instance through the new keyword. The following is the basic usage of promise

 <!--promise basic usage of-->
 const promise = new Promise((resolve, reject) => {
  // code for asynchronous operations
  if (success){
    resolve(value);
  } else {
    reject(error);
  }
});
copy code

The Promise constructor accepts a function as a parameter. The two parameters of the function are resolve and reject, which are two functions provided by the JavaScript engine. Asynchronous operations have two outcomes: success or failure

 

  • The resolve function is triggered when the asynchronous operation changes from the pending state (execution in progress) to the resolved (successful state), passing the result of the successful operation;
  • The reject function is triggered when the asynchronous operation changes from the pending state (execution in progress) to the rejected (failed state), passing the result of the failed operation.

Note that the status of the promise can only be changed by pending => fulfilled/rejected, once it is modified, it cannot be changed again

promise object method

Promise.prototype.then()

So the question is, just now we mentioned that the corresponding function is triggered after the Promise state changes, so where should we write the code to handle the state change? That's right, the then() method. Then method is defined in the prototype object Promise On prototype, its function is to add a callback function when the state changes for Promise instance. The first parameter of then method is the callback function of the resolved state, and the second parameter (optional) is The callback function of the rejected state

 <!--promise then method-->
 const promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // Status is pending => fulfilled
 }).then(result => { 
    console.log(result); // 'fulfilled' 
 }, reason => { 
    
 })
copy code
const promise = new Promise((resolve, reject) => {
  reject('rejected '); // Status is pending => rejected
}).then(result => { 
    
}, reason => { 
   console.log(reason); // 'rejected'
})
copy code

As mentioned above, once the promise state is modified, it cannot be changed, it can only be changed by pending => fulfilled or pending => rejected

Promise uses a chain call. then() registers a callback function for promise, and the parameters are the return results of the previous task. Therefore, the function in then in the chain call must return a result or a new promise object before the subsequent then callback to receive

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

Promise.prototype.catch()

The Promise.prototype.catch method is an alias of .then(null, rejection) or .then(undefined, rejection), that is, the callback function when an error occurs in the asynchronous operation. In addition, the callback function in the then() method also has an error. caught by catch().

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

At this point, careful students will find that since the second parameter of my then() method can be used to throw errors, why use this catch() method. In fact, there is still a difference. In the chain operation, any synchronous or asynchronous error thrown by the promise can be caught by the then() method, while the reject handles the error of the current promise. Therefore, it is recommended not to define the callback function of the reject state (that is, the second parameter of then) in the then method, and always use the catch method, which is closer to the synchronous writing method (try/catch).

<!--promise catch method-->
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 code

Promise.prototype.finally()

The finally() method was introduced as a standard in ES2018. This method indicates that no matter what state of the promise, after the then() or catch() methods are executed, the finally() method will be executed at the end.

<!-- promise finally method-->
const promise = new Promise((resolve, reject) => {})
.then(result => {})
.catch(err => {})
.finally(() => {})
copy 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"] The result order is the same as the order of the promise instance array
}).catch(err => {
  consolo.log(err)
});
copy code

Only when the status of promise1, promise2, and promise3 becomes fulfilled, the status of Promise.all becomes fulfilled. At this time, the return values ​​of promise1, promise2, and promise3 form an array and pass it to the callback function of Promise.all.

As long as one of promise1, promise2, and promise3 is rejected, the status of Promise.all becomes rejected, and the return value of the first rejected instance will be passed to the callback function of Promise.all

When working on a project, we often encounter multiple requests for a page. We can use promise.all encapsulation to facilitate request management.

Similar axios also has 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 code

In the above code, as long as an instance of promise1, promise2, and promise3 changes the state first, the state of the promise changes accordingly. The return value of the Promise instance that changed first is passed to p's callback function.

3. Application of Promise

combine with axios

In the project, we often encounter the need to repackage axios according to the business, such as request interception to set token and Content-Type, and response interception to set different responses according to different status codes. In addition, we can also repackage 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)
          })
      });
    }
}
<!--use the whole user Module requests 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 code

Load images asynchronously

An example of asynchronously loading images with promise s

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


author: cat under the window

 

 

Tags: ECMAScript Front-end

Posted by maineyak on Wed, 20 Jul 2022 21:50:58 +0530