“Gold three silver four, gold nine silver ten”, are to harvest the season. In the face of all kinds of interview questions, all kinds of concepts, principles have to remember, very boring. This article talks about Promise for interview questions and practical use.

What is Promise?

Promise is an important concept in JS asynchronous programming, which abstracts objects asynchronously and is one of the more popular Javascript asynchronous programming solutions at present. A Promise is an idea, a solution, or an object that solves an asynchronous problem. One area where asynchrony is often used in JS is Ajax interaction. For example, in the ES5 era, jQuery ajax uses SUCCESS to accomplish asynchronous:

$.ajax({
   url:'/xxx',
   success:()=>{},
   error: ()=>{}
})Copy the code

This approach makes it clear to the reader which parts of the code are successful and failed callback functions for Ajax requests. But here’s the thing: when a request requires multiple interfaces in a row, the code seems to be in a mess:

// first time $.ajax({url:'/xxx', success ()=>{// second time $.ajax({url:'/xxx', success ()=>{// third time $.ajax({url:'/xxx'Success: () = > {/ /} are likely, error: () = > {}})}, error: () = > {}})}, error: () = > {}})Copy the code

This code might be easy to understand because of the success and error functions, but it becomes a tricky problem when we change the requirements. This is callback hell.

Of course, this is the ES5 era. When JS came to es6, the Promise revolutionized asynchrony. Promise provides a then to provide callback functions for asynchrony:

$.ajax({
    url:'/xxx'}). Then (()=>{// successful callback}, ()=>{// failed callback})Copy the code

The advance is that you can continue writing the Promise object in the THEN method and return it, and then continue calling the THEN for the callback.

The use of the Promise

Having said what promises are, let’s take a look at how they can be used. First, a Promise is an object, so we create a new one using new. It is then passed a function that takes two arguments, resolve and reject, which are also functions. Next, we invoke this Promise using then:

const fn = new Promise(function (resolve, reject) {
  setTimeout(()=>{
    letNum = math.ceil (math.random () * 10) // Assume num is 7if(num > 5) {resolve(num)else {
      reject(num)
    }
  },2000)
})
fn.then((res)=>{
  console.log(res) // 7
},(err)=>{
  console.log(err)
})Copy the code

This is the simplest use of promises. Assume that a random number of 7 is generated after 2 seconds, so the resolve callback runs, then the first function, console.log(7). Assume that a random number of 3 is generated after 2 seconds, so the reject callback runs, then the second function, console.log(3).

So you probably said, “Promise, it wouldn’t be a big deal if it was all you could do?” The advance of promises is that we can continue writing the Promise object in the THEN method and return it, and then continue calling the THEN for callback operations:

fn = new Promise(function (resolve, reject) {
  let num = Math.ceil(Math.random() * 10)
  if (num > 5) {
    resolve(num)
  } else{reject (num)}}) for the first time, / / callback fn. Then ((res) = > {the console. The log (` res = = >${res}`)
  return new Promise((resolve,reject)=>{
    if(2*res>15){
      resolve(2*res)
    }else{reject (2 * res)}})}, (err) = > {the console. The log (` err = = >${err}`)}). Then ((res) = > {/ / callback for the second time the console. The log (res)}, (err) = > {the console. The log (` err = = >${err}`)})Copy the code

This replaces the creation of nested callback hell above, similar to ES5-era jQurey’s success, and makes the code much cleaner. Resolve here is equivalent to success before.

The principle of Promise

Inside the Promise, there is a state manager, which includes three states: Pending, depressing and Rejected.

(1) The state of the promise object is initialized as pending.

(2) When you call resolve(success), there will be pending => depressing.

Reject => Reject; reject => Reject;

Thus, in the code above, resolve(num) actually changes the state of a promise from pending to depressing, and then passes a value to then’s success return function, reject and vice versa. But remember that promsie status can only be modified by pending => depressing/Rejected. This state cannot be changed once modified.

This is a big pity. When the state is fulfilled (fulfilled), the successful callback function of THEN will be called, and num will be accepted from the above, and then the operation will be performed. The promise.then method returns a new promise object each time it is called, so it can be chained (resolve or reject).

Several approaches to Promise

then

The then method is used to register callback functions when the state becomes progressively or reject:

// This is a big pity, which is very depressing. // onRejected is the reason why the promise fails. Then (onRejected, onRejected);Copy the code

It is important to note that the THEN methods are executed asynchronously.

Const promise = new promise ((resolve, reject) => {resolve()'fulfilled'); // state by pending => depressing}); promise.then(result => { // onFulfilled console.log(result); //'fulfilled'}, reason => {// reject(reject); // reject(reject); // reject(reject); reject) => { reject('rejected'); // State by pending => Rejected}); This is a big pity. Then (result => {// onRejected console.log(rejected)), reason => {// onRejected console.log(rejected); //'rejected'
})Copy the code

catch

Catch in chained writing catches an exception sent in the previous THEN.

fn = new Promise(function (resolve, reject) {
  let num = Math.ceil(Math.random() * 10)
  if (num > 5) {
    resolve(num)
  } else{ reject(num) } }) fn.. then((res)=>{ console.log(res) }).catch((err)=>{ console.log(`err==>${err}`)})Copy the code

Catch = then(null,onRejected) catch = then(onRejected)

Resolve, reject

Promise. Resolve returns a fulfilled Promise object, and Promise. Reject returns a fulfilled Promise object.

Promise.resolve('hello').then(function(value){
    console.log(value);
});

Promise.resolve('hello'); // const promise = new promise (resolve => {resolve())'hello'); }); / / reject insteadCopy the code

all

Static method: static method: static method: static method: static method: static method: static method: static method

var   p1 = Promise.resolve(1),
      p2 = Promise.reject(2),
      p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then((res)=>{
    //thenThe console.log(results) method will not be executed; }). Catch ((err)=>{// The catch method will be executed, and the output is: 2 console.log(err); });Copy the code

Once one of the promise objects is in the rejected state, the return value of All is Rejected.

This is a big pity. When the return state of these functions as parameters is fulfilled, the output time will depend on who is running slowly:

let p1 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('1s'Resolve (1)},1000)})let p10 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('10s'Resolve (10)},10000)})let p5 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('5s') / / 5 s after output resolve (5)}, 5000)}) Promise. All ([p1, p10, p5]), then ((res) = > {the console. The log (res); // Last output})Copy the code

When this code is run, it will output after 10 seconds [1,10,5] according to the rule of who is the slowest runner. Over, all

race

The promise.race() method can also handle an array of promise instances, but unlike promise.all(), it literally means racing, which is much easier to understand. That is, the element instance in the array that changes state first passes down its state and asynchronous results. But the rest will continue.

let p1 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('1s'Resolve (1)},1000)})let p10 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('10s'Resolve (10) // do not pass},10000)})let p5 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('5s') / / 5 s after output resolve (5) / / no}, 5000)}) Promise. Race ([p1, p10, p5]), then ((res) = > {the console. The log (res); // Last output})Copy the code

So, at the end of this code, our result is zero

1s
1
5s
10sCopy the code

We can do timeout based on the race property:

// Request an image resourcelet requestImg = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){ resolve(img); }}); // The delay function is used to time the requestlet timeOut = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('Image request timed out');
        }, 5000);
    });

Promise.race([requestImg, timeout]).then((res)=>{
    console.log(res);
}).catch((err)=>{
    console.log(err);
});Copy the code

Promise related interview questions

1.

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
});
promise.then(() => {
    console.log(3);
});
console.log(4);Copy the code

The output is 1,2,4,3.

The then method is executed asynchronously.

2.

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
    reject('error')
  }, 1000)
})
promise.then((res)=>{
  console.log(res)
},(err)=>{
  console.log(err)
})Copy the code

Result: Success

Once a Promise status is changed, it cannot be changed again.

3.

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)Copy the code

Output: 1

The parameters of Promise’s then method are expected to be functions, and value penetration occurs when non-functions are passed in.

4.

setTimeout(()=>{
  console.log('setTimeout')})let p1 = new Promise((resolve)=>{
  console.log('Promise1')
  resolve('Promise2')
})
p1.then((res)=>{
  console.log(res)
})
console.log(1)Copy the code

Output result:

Promise1 1 Promise2 setTimeout

The entire script is placed in the MacroTask queue. When setTimeout is reached, a new MacroTask queue will be created. However, promise.then is placed in another task queue, microTask Queue. The script execution engine takes a task from a MacroTask queue and executes it. Execute all microTask queues in sequence, and set setTimeout to macroTask queue in sequence. (refer to www.zhihu.com/question/36.)

5.
Promise.resolve(1)
    .then((res) => {
        console.log(res);
        return 2;
    })
    .catch((err) => {
        return 3;
    })
    .then((res) => {
        console.log(res);
    });Copy the code

Output: 1 2

The Promise first resolves (1), then executes the then function, so it prints 1, and then returns 2 in the function. Because it is resolve, the catch function does not execute. Instead, it executes the second then function, and thus prints 2.

6.

const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('开始');
resolve('success');
}, 5000);
});
 
const start = Date.now();
promise.then((res) => {
console.log(res, Date.now() - start);
});
 
promise.then((res) => {
console.log(res, Date.now() - start);
});Copy the code

Output result:

start

success 5002

success 5002

The.then or.catch of a promise can be called multiple times, but the Promise constructor is executed only once. Or once the internal state of the promise changes and it has a value, then every subsequent call to.then or.catch will get that value directly.

7.

let p1 = new Promise((resolve,reject)=>{
  let num = 6
  if(num<5){
    console.log('resolve1')
    resolve(num)
  }else{
    console.log('reject1')
    reject(num)
  }
})
p1.then((res)=>{
  console.log('resolve2')
  console.log(res)
},(rej)=>{
  console.log('reject2')
  let p2 = new Promise((resolve,reject)=>{
    if(rej*2>10){
      console.log('resolve3')
      resolve(rej*2)
    }else{
      console.log('reject3') reject(rej*2) } })return p2
}).then((res)=>{
  console.log('resolve4')
  console.log(res)
},(rej)=>{
  console.log('reject4')
  console.log(rej)
})Copy the code

Output result:

Reject1 reject2 resolve3 resolve4 12

A Promise object can be written and returned in the then method.

8. Highlight !!!! Make a simple Promise
function Promise(fn){
  var status = 'pending'
  function successNotify(){
      status = 'fulfilled'// This will be a big pity. Apply (undefined, arguments)function failNotify(){
      status = 'rejected'// The status changes to Rejected todothen. apply(undefined, arguments)function toDoThen() {setTimeout(()=>{// Ensure that the callback is executed asynchronouslyif(status === 'fulfilled') {for(leti =0; i< successArray.length; I ++) {successArray[I]. Apply (undefined, arguments)//thenThe callback function inside}}else if(status === 'rejected') {for(leti =0; i< failArray.length; I ++) {failArray[I]. Apply (undefined, arguments)// ExecutethenVar successArray = [] var failArray = [] fn. Call (undefined, successNotify, failNotify)return {
      then: function(successFn, failFn){
          successArray.push(successFn)
          failArray.push(failFn)
          returnUndefined // return a Promise}}}Copy the code

Resolve and reject in a Promise are used to change the state and pass parameters of a Promise. The parameters in then must be functions that execute as callbacks. Therefore, when a Promise changes state, the callback function is called, and the callback function to execute is selected based on the state.

conclusion

First, a Promise is an object that, as it literally means, represents a time in the future when the outcome will be known, uninfluenced by external factors. Once the Promise is triggered, its state can only become a pity or rejected, and it has changed irrevocably. The Promise constructor takes a function as an argument. The function’s parameters are resolve and reject, which convert the Promise state from pending to pity or Rejected, and pass the return value of success or failure. Then has two functions that act as callbacks when the Promise state changes, accepting the passed parameters and calling the corresponding functions when the Promise state changes. The procedure for the callback in THEN is asynchronous. The catch method is a wrapper (syntactic sugar) for. Then (null,rejectFn) that specifies the callback function in case of an error. In general, it is recommended not to define callback functions with the Rejected state in then and to use catch instead. Both All and race are race functions. The end time of all depends on the slowest one. Once the Promise function as a parameter has a state of Rejected, the total Promise state is Rejected. The end time of the race depends on the fastest one. Once the fastest Promise state changes, the total Promise state of that one changes to the corresponding state, and the rest of the parameters continue.

Of course in the ES7 era, there are await/async asynchronous schemes, which we will talk about later.