First understand the concept of the following concepts:

Synchronization: Only one task can be executed at a time. After this task is completed, the next task can be executed, which blocks other tasks.

Asynchronous: Multiple tasks can be performed together.

Callback hell: The nesting of callback functions, resulting in too many layers of code that are difficult to understand and maintain.

I. The meaning of Promise

Let’s take a look at the following Promise meanings and features:

  • Promise is a solution for asynchronous programming that is more reasonable and powerful than traditional solutions (callback functions and events);

  • A Promise is simply a container that holds the result of an 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;

  • The Promise object represents an asynchronous operation with three states: Pending, fulfilled and Rejected. This is a pity.

Features of Promise objects:

1. The status of the object is not affected by the outside world.

Only the result of an asynchronous operation can determine which state is currently in, and no other operation can change that state.

2. Once the state changes, it will never change.

There are only two possibilities for a Promise object to change state:

  • Change from Pending to depressing
  • From pengding to Rejected

As soon as these two states are discovered the state is fixed and will not change and will remain as it is called resolved

3. Each Promise instance has a THEN method.

4. Each time a Promise is executed, a new Promise instance is returned;

With the Promise object, you can express asynchronous operations as a flow of synchronous operations, eliminating the problem of asynchronous layers of nested functions (callback hell for short).

When it comes to promises, the first thing that comes to mind is asynchronous chain calls.

Disadvantages of Promise objects:

  1. Unable to cancel the Promise. Once created it will be executed immediately, can not be cancelled midway;

  2. If you don’t set a callback function, the Promise throws an error internally and doesn’t react externally;

  3. When in the Pending state, there is no way to know which node is currently progressing (just started or about to complete).

Advantages of using Promise objects:

  1. Can solve the asynchronous nesting problem (callback hell);
  2. Can solve multiple asynchronous concurrency problems;

Two, basic usage

Note that in order to facilitate the writing, the “resolved” in this chapter only refers to the regrettable state, excluding the rejected state.

1. Create a Promise instance

ES6 specifies that a Promise object is a constructor that uses new to generate a Promise instance.

let promise = new Promise(() = > {// Executor executor, which is characterized by immediate execution

})
Copy the code

This Promise instance is a class that allows you to take a function as an argument, called an Executor executor, which is typically executed immediately. The following will be printed immediately:

let promise = new Promise(() = > {//executor executor, which executes immediately
    console.log("Immediate execution");//Promise has three states. The default state is Pending
}) 
console.log("222");

/* Console prints */

// Execute immediately
/ / 222
Copy the code

2. Resolve,rejected; then

The function accepts two parameters, resolve and Rejected, and each Promise instance has one

Then method. (Note: Resolve and Rejected must be used together with THEN.)

Then places two functions:

  • Onfulfils the logic to be performed successfully by an example
  • The onRejected instance fails the logic to be executed
let promise = new Promise((resolve,rejected) = > {
  resolve('success');// If resolve is called, then will succeed;
}).then(data= > {/ / success
  console.log(data);
},err= > {/ / fail
  console.log(err);
})

/* Console prints */
/ / success


let promise = new Promise((resolve,rejected) = > {
  rejected('failure');// If (rejected) then (rejected) then (rejected)
}).then(data= > {/ / success
  console.log(data);
},err= > {/ / fail
  console.log(err);
})

/ / fail


// The code above can also be written like this:
let promise = new Promise((resolve, rejected) = > {
    resolve('success');
})
promise.then(data= > {
    console.log(data);
}, err= > {
    console.log(err);
})
Copy the code

The state of a Promise instance does not change once the state changes, so resolve and Rejected will not take effect. And vice versa.

let promise = new Promise((resolve,rejected) = > {
  resolve('success');
  rejected('failure');// This step will not be executed
}).then(data= > {/ / success
  console.log(data);
},err= > {/ / fail
  console.log(err);
})

/ / success
Copy the code

If the Promise instance reports an internal error, it becomes a failed state and does not execute the following method:

let promise = new Promise((resolve, rejected) = > {//executor executor, which executes immediately
    throw new Error('Internal throw error');// If an error is thrown internally, then the state becomes failed
    resolve('success');// This step will not be executed
}).then(data= > {/ / success
    console.log(data);
}, err= > {/ / fail
    console.log(err);
})

//Error: An internal Error is thrown
// ...
Copy the code

Principle of Promise instance

Write a basic Promise by hand

Create a promise. Js, create a promise class, and export:

//promise.js:
class Promise {};module.exports = Promise;/ / export
Copy the code

Introduce the previous code into promise.js:

/ / callBack. Js:
let Promise = require('./promise');
// Introduce a custom Promise. The following Promise is a custom Promise

let promise = new Promise((resolve, rejected) = > {
    resolve('success');
}).then(data= > {
    console.log(data);
}, err= > {
    console.log(err);
})
Copy the code

Define a base version of the Promise from the callback.js Promise instance, without considering other exceptions:

(1) Create public THEN methods

A Promise instance has three states, and the THEN methods will be executed in either state, so the THEN methods are a common property.

//promise.js:
class Promise {
  then(){}};module.exports = Promise;/ / export
Copy the code

Each Promise instance has its own three states, so put them into the corresponding constructor.

The state of the Promise instance is available in the constructor, and since these three states are used frequently, we can store them as a constant.

//promise.js:
const PENDING = "PENDING";// Wait state
const RESOLVED = "RESOLVED";// Success status
const REJECTED = "REJECTED";// Failed state
class Promise {
  
  constructor(){// constructor
    this.status = PENDING;// The default pending state of the Promise instance
  }
  
  then(){}};module.exports = Promise;/ / export
Copy the code

We pass a function (i.e., an executor) in the new Promise instance of callbacks.js, and this function executes immediately, so we pass an executor executor to the constructor as well:

//promise.js:
const PENDING = "PENDING";// Wait state
const RESOLVED = "RESOLVED";// Success status
const REJECTED = "REJECTED";// Failed state
class Promise {
  
  constructor(executor){// constructor
    this.status = PENDING;// The default pending state of the Promise instance
    
    executor();// The default executor executes immediately
  }
  
  then(){}};module.exports = Promise;/ / export
Copy the code

When the executor executes a new Promise instance of callbacks.js, it passes two functions that belong to the current Promise instance and need not be retrieved from then, so we can declare two functions from constructor: a success function and a failure function. And pass both functions to the executor executor:

//promise.js:
const PENDING = "PENDING";// Wait state
const RESOLVED = "RESOLVED";// Success status
const REJECTED = "REJECTED";// Failed state
class Promise {
  
  constructor(executor){// constructor
    this.status = PENDING;// The default pending state of the Promise instance
    // Success function
    let resolve = () = >{}// Fail function
    let reject = () = > {
    
    }
    
    
    executor(resolve,reject);// The default executor executes immediately
  }
  
  then(){}};module.exports = Promise;/ / export
Copy the code

Both successful and failed functions accept a value, value and Reason, because these values are also used in THEN, so we need to define a variable. We need to update the state of the Promise after executing the success and failure functions:

//promise.js:
const PENDING = "PENDING";// Wait state
const RESOLVED = "RESOLVED";// Success status
const REJECTED = "REJECTED";// Failed state
class Promise {
  
  constructor(executor){// constructor
    this.status = PENDING;// The default pending state of the Promise instance
    
    this.value = undefined;// Success value
    this.reason = undefined;// Cause of failure
    
    // Success function
    let resolve = (value) = > {
      this.value = value;
      this.status = RESOLVED;// The Promise instance status is updated to successful: Resolved
    }
    
    // Fail function
    let reject = (reason) = > {
      this.reason = reason;
      this.status = REJECTED;// The STATE of the Promise instance changes to the Failed state: Rejected state
    }
    
    
    executor(resolve,reject);// The default executor executes immediately
  }
  
  then(){}};module.exports = Promise;/ / export
Copy the code

Since the state of a Promise instance cannot be changed again once it has changed, that is to say, we cannot call resolve, reject, so we must add state judgment, and execute the function only when the state is waiting for state pending:

//promise.js:
const PENDING = "PENDING";// Wait state
const RESOLVED = "RESOLVED";// Success status
const REJECTED = "REJECTED";// Failed state
class Promise {
  
  constructor(executor){// constructor
    this.status = PENDING;// The default pending state of the Promise instance
    
    this.value = undefined;// Success value
    this.reason = undefined;// Cause of failure
    
    // Success function
    let resolve = (value) = > {
      if(this.status === PENDING){//// shield call, call a successful function cannot call a failed function
        this.value = value;
        this.status = RESOLVED;// After the call succeeds, the Promise state becomes Resolved}}// Fail function
    let reject = (reason) = > {
      if(this.status === PENDING){
        this.reason = reason;
        this.status = REJECTED;After //// fails, the Promise state changes to the Rejected state
      }
    }
    
    
    executor(resolve,reject);// The default executor executes immediately
  }
  
  then(){}};module.exports = Promise;/ / export
Copy the code

An exception may be thrown during execution, so add a try… The catch function, which calls reject if an exception is thrown internally:

Next we call the then() method, which can place two functions:

  • Onfulfils the logic to be performed successfully by an example
  • The onRejected instance fails the logic to be executed

So which method is called when? This depends on the state of the Promise instance:

Promise instances also have subscription publishing capabilities:

To be continued, follow-up updates ~~

Promise.js complete code:

//promise.js:
const PENDING = "PENDING";// Wait state
const RESOLVED = "RESOLVED";// Success status
const REJECTED = "REJECTED";// Failed state
class Promise {
  
  constructor(executor){// constructor
    this.status = PENDING;// The default pending state of the Promise instance
    
    this.value = undefined;// Success value
    this.reason = undefined;// Cause of failure
    
    this.onResolveCallBacks = [];// Array of successful callbacks
    this.onRejectCallBacks = [];// Array of failed callbacks
    
    // Success function
    let resolve = (value) = > {
      if(this.status === PENDING){//// shield call, call a successful function cannot call a failed function
        this.value = value;
        this.status = RESOLVED;// After the call succeeds, the Promise state becomes Resolved
         this.onResolveCallBacks.forEach(fn= > fn())/ / release}}// Fail function
    let reject = (reason) = > {
      if(this.status === PENDING){
        this.reason = reason;
        this.status = REJECTED;After //// fails, the Promise state changes to the Rejected state
        this.onRejectCallBacks.forEach(fn= > fn())/ / release}}// An internal error may occur when the actuator executes:
    try{
        executor(resolve,reject);// The default executor executes immediately, taking resolve,reject as arguments
    }catch(error){
      reject(error);// If an error occurs during the execution of the actuator, it is equivalent to calling the failed method}}then(onfulfilled,onrejected){/// Then currently has two parameters: onfulfilled, onRejected
    // Synchronization condition: execute logic after success
    if(this.status === RESOLVED){
       onfulfilled(this.value);
    }
    
    // Synchronization: execute logic after failure
    if(this.status === PENDING){
       onrejected(this.reason);
     }
    
     // If it is asynchronous, subscribe first
    if (this.status === PEDING) {
      this.onResolveCallBacks.push(() = > {
        // todo
        onfulfilled(this.value)
      })

      this.onRejectCallBacks.push(() = > {
        // todo
        onrejected(this.value)
      })
        }
  }
};
module.exports = Promise;/ / export
Copy the code

A. Promise B. Promise C. Promise D. Promise

1, implement function sleep, first print A, 1 second later print B, what is the solution?

(1) Achieve through Promise:

console.log('A');
function sleep(time) {
  return new Promise((resolve) = > {
    setTimeout(() = >{ resolve(); }, time); })}; sleep(1000).then(() = > {
  console.log('B');
});
        
// Output A first, then output B after A delay of 1 second

/ / or
console.log('A');
const sleep = ((time) = >{
  return new Promise((resolve) = >{
    setTimeout(() = >{
      resolve();
    },time)
  })
})
sleep(1000).then(() = >{
  console.log('B');
})
Copy the code

(2) Implemented through Async/AWIAT:

const sleep = ((time) = >{
  return new Promise((resolve) = >{
    setTimeout(() = >{
      resolve();
    },time)
  })
})

async function sleepAsync(){
  await sleep(1000);
  console.log('B');
}
Copy the code

(3) Implement from Generator and yield

console.log("A");
const sleep = ((time) = >{
  return new Promise((resolive) = >{
    setTimeout(() = >{
      resolve();
    },time)
  })
})

function* sleepGenerator(time){
  yeild sleep(time);
}

sleepGenerator(1000).next().value.then(() = >{
  console.log("B");
})
Copy the code

Similar interview questions:

There are often business requirements that require you to wait a few seconds before taking the next step

2, the red light three seconds once, the green light one second, yellow light two seconds once; How do I make three lights turn on again and again? (Promse + recursion)

Three lighting functions already exist:

Ideas:

Three seconds on a red light, green light for a second time, yellow light 2 seconds on time, which means that for 3 seconds, performs a function of red, 2 seconds, perform a green function, 1 second to perform a yellow function, alternating repeated lights, which means that in this order has been executed this three functions, this step can use recursion to achieve.

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

const sleep = ((time,fn) = >{
  return new Promise((resolve) = >{
    setTimeout(() = >{
      fn();// Which light is on
      resolve();
    },time)
  })
})

let step = (() = >{
  Promise.resolve().then(() = >{
    return sleep(3000,red);
  }).then(() = >{
    return sleep(2000,green);
  }).then(() = >{
    return sleep(1000,yellow);
  }).then(() = >{ step(); })})Copy the code

3. Output the following execution result:

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

The Promise. Resolve method returns a new Promise object in the resolved state, if the parameter is a raw value or an object that does not have then methods. The arguments to the promise. resolve method are also passed to the callback function.

The then method takes a function as an argument, and if it passes something other than a function, it actually interprets it as THEN (NULL), which causes the result of the previous Promise to penetrate below.

Application scenarios of Promise

.

In learning writing, continue to supplement and update, record bad places hope to point out modification, common progress ~

References:

ECMAScript 6 Getting Started – Promises

Interview questions about Promise

Handwritten Promise20 line

Analyze the internal structure of Promise, step by step to achieve a complete Promise class that can pass all Test cases

Write a Promise