The Promise object is a new asynchronous programming solution introduced by ES6 that is more reasonably powerful than traditional callback functions.

concept

A Promise object is a proxy object (proxy for a value), and the proxied value may not be known at the time the Promise object is created. It allows you to bind handlers for success and failure of asynchronous operations. This allows asynchronous methods to return values as synchronous methods do, but instead of immediately returning final execution results, a Promise object that represents future results.

state

A Promise has the following states:

  • Pending: Indicates the initial status.
  • This is a pity.
  • Rejected: Failed.



What does it say? Half a sentence did not understand, sharpening the knife… Ing)

Take a fall

(Everybody eldest brother don’t worry…… I was wrong. Here’s an example.

Let’s say I have a girlfriend (well, I don’t, programmed apes can’t have girlfriends!!). Her birthday is tomorrow. At noon today, I promised to celebrate her birthday and give her a surprise. Then my promise to her starts to enter the pending state, waiting for the arrival of tomorrow. This is a big pity. If I will accompany her to dinner on time tomorrow and surprise her with a gift, then I will fulfil this promise, which will be fulfilled successfully instead of the waiting state. Once this promise is fulfilled, there will be a result. But if I’m busy tomorrow and I have to work late and I don’t fulfill this promise, THEN I’m in a state of rejected (rejected)! .

Promise basically did something, okay?

Basic usage

ES6 specifies that a Promise object is a constructor that generates a Promise instance. The Promise constructor takes a function as an argument, resolve and reject.

Basic Usage 1

Let p = new Promise((resolve, reject) => {setTimeout(() => {// async if(true) {// async resolve(' I am async ')); }else {// reject(' I'm returned asynchronously '); }}, 1000) console.log(' I entered first '); // The constructor executes immediately}) p.chen (res => {console.log(res); }, err => {console.log(err); // I am asynchronous return failure data})Copy the code

Basic Usage 2

Catch instead of.catch, which is probably the more common way to catch exceptions. Anyway, xiaobian prefers this kind of writing method…

Let p = new Promise((resolve, reject) => {setTimeout(() => {// async if(true) {// async resolve(' I am async ')); }else {// reject(' I'm returned asynchronously '); }}, 1000) console.log(' I entered first '); // The constructor executes immediately}) p.chen (res => {console.log(res); }). Catch (err => {console.log(err); // I am asynchronous return failure data})Copy the code

Implementing manual Promises

Synchronous handwriting version (enough for interview handwriting ^-^)

With a quick reminder of basic usage, let’s get down to business.

Let’s construct it in the form of a constructor, there are also useful ES6 Class to implement the form, interested partners can see some degree of their own.

Start by defining the Promise constructor.

function myPromise() {
	
}
Copy the code

Looking at the basic usage above, you can see that the constructor takes a function as an argument and executes it immediately (console.log(‘ I typed first ‘); // The constructor executes immediately), and the function takes two more callbacks as arguments (Promise objects actually work with the syntactic sugar of callbacks).

function myPromise(constructor) {
  function resolve() {

  }
  function reject() {

  }
  constructor(resolve, reject);
}
Copy the code

Then we see the.then() method, which can be accessed through an instance of the constructor, and we obviously have to create the method on the prototype of the constructor. Please click), and it receives two functions (see the basic. Then () call above), namely the success and failure callback.

myPromise.prototype.then = function(fulfilled, rejected){
    
}
Copy the code

We then refine the myPromise() constructor, which has three states and changes the Promise state by performing different callbacks (resolve() and reject()).

function myPromise(constructor) { let _this = this; _this.status = 'pending'; Function resolve() {_this.status = 'pity '; } function reject() {_this.status = 'rejected'; } constructor(resolve, reject); }Copy the code

Of course, as we showed in the example above, once the state has changed it cannot be changed again, so we also need to make some judgments between success and failure callbacks to ensure that they are done only once.

function myPromise(constructor) { let _this = this; _this.status = 'pending'; Function resolve() {if(_this.status === 'pending') {_this.status = 'depressing '; }} function reject() {if(_this.status === 'pending') {_this.status = 'rejected'; }} constructor(resolve, reject); }Copy the code

The.then() method will receive the result in the callback (resolve(‘ I am returned asynchronously as success data ‘);). So we need to save the result in the success and failure callback to ensure that the result is returned when the.then() method is called.

function myPromise(constructor) { let _this = this; _this.status = 'pending'; _this.result = undefined; _this.reason = undefined; Function resolve(result) {if(_this.status === 'pending') {_this.status = 'depressing '; _this.result = result; } } function reject(reason) { if(_this.status === 'pending') { _this.status = 'rejected'; // Failed state _this.reason = reason; } } constructor(resolve, reject); }Copy the code

Once the result is saved, the myPromise() constructor is pretty much done, leaving the.then() method, which only needs to do one thing, to reasonably return the corresponding result.

myPromise.prototype.then = function(fulfilled, rejected) { let _this = this; // This is the same as this. If you don't understand this, you should check the content of the prototype chain. switch(_this.status) { case 'fulfilled': fulfilled(_this.result); break; case 'rejected': rejected(_this.reason); break; }}Copy the code

At this point, the simple structure of the Promise object is basically implemented, so let’s briefly test it.

Let p = new myPromise((resolve, reject) => {if(true) {// resolve(' reject '); }else {// reject(' reject data '); } console.log(' I entered first '); // The constructor executes immediately}) p.chen (res => {console.log(res); // Success data}, err => {console.log(err); // Failed data})Copy the code

Is that it? Of course not, this is just an implementation of synchronous processing.

Asynchronous handwriting

First we know JS is a single thread, sequential execution order from top to bottom, if you encounter an asynchronous task will throw aside to continue to perform the first, and then look back, asynchronous tasks and tasks can be divided into macro and micro tasks, concrete is a concept of what is not said it point (point), anyway, keep in mind that if is asynchronous operations, The asynchronous code must be executed before it, this should be paid attention to!!

The.then() method is called without actually calling the resolve() and reject() callbacks to change the state of the Promise object. So the state of the Promise object is still pending, and we do not know whether the success or failure callback in the.then() method is executed, and there is no associated result to return. So I need to save the success or failure callback and wait for the right time to execute the call.

Create a place to hold callbacks on the myPromise() constructor. Note that the Promise object supports chained calls, so multiple. Then () methods may be called in succession, so there may be multiple groups of successful and failed callbacks.

function myPromise(constructor) { let _this = this; _this.status = 'pending'; _this.result = undefined; _this.reason = undefined; // Save the result of a failure _this. callbacks = []; _this.rejectedCallbacks = []; Function resolve(result) {if(_this.status === 'pending') {_this.status = 'depressing '; _this.result = result; _this.fulfilledCallbacks.forEach(fulfilledCb => { fulfilledCb(); }); } } function reject(reason) { if(_this.status === 'pending') { _this.status = 'rejected'; // Failed state _this.reason = reason; _this.rejectedCallbacks.forEach(rejectedCb => { rejectedCb(); }); } } constructor(resolve, reject); }Copy the code

.then() stores the successful and failed callback functions until the right time to execute the call.

myPromise.prototype.then = function(fulfilled, rejected) { let _this = this; // This is the same as this. If you don't understand this, you should check the content of the prototype chain. switch(_this.status) { case 'fulfilled': fulfilled(_this.result); break; case 'rejected': rejected(_this.reason); break; case 'pending': _this.fulfilledCallbacks.push(() => { fulfilled(_this.result); }); _this.rejectedCallbacks.push(() => { rejected(_this.reason); }); break; }}Copy the code

Test code (basically the code used above)

Let p = new myPromise((resolve, reject) => {setTimeout(() => {// async if(false) {// async resolve(' I am async ')); }else {// reject(' I'm returned asynchronously '); }}, 1000) console.log(' I entered first '); // The constructor executes immediately}) p.chen (res => {console.log(res); }, err => {console.log(err); // I am asynchronous return failure data})Copy the code

Is that it? Why? I’m sure it hasn’t happened yet.

Let’s have the Promise object throw an error.

Let p = new myPromise((resolve, reject) => {throw new Error(' Error ')}) p.hen (res => {console.log('res:' + res); }, err => {console.log('err:' + err); // I am asynchronous return failure data})Copy the code

This error does not seem to go into the failed callback as we would expect it to.

Resolve this error.

try{
  constructor(resolve, reject);
}catch(e) {
  reject(e);
}
Copy the code

There you go. That’s perfect. That’s good. You’re good.

Complete source code

function myPromise(constructor) { let _this = this; _this.status = 'pending'; _this.result = undefined; _this.reason = undefined; // Save the result of a failure _this. callbacks = []; _this.rejectedCallbacks = []; Function resolve(result) {if(_this.status === 'pending') {_this.status = 'depressing '; _this.result = result; _this.fulfilledCallbacks.forEach(fulfilledCb => { fulfilledCb(); }); } } function reject(reason) { if(_this.status === 'pending') { _this.status = 'rejected'; // Failed state _this.reason = reason; _this.rejectedCallbacks.forEach(rejectedCb => { rejectedCb(); }); } } constructor(resolve, reject); } myPromise.prototype.then = function(fulfilled, rejected) { let _this = this; // This is the same as this. If you don't understand this, you should check the content of the prototype chain. switch(_this.status) { case 'fulfilled': fulfilled(_this.result); break; case 'rejected': rejected(_this.reason); break; case 'pending': _this.fulfilledCallbacks.push(() => { fulfilled(_this.result); }); _this.rejectedCallbacks.push(() => { rejected(_this.reason); }); break; }}Copy the code

Then () then() then() Er… Why don’t we just keep it in suspense? If someone wants to see it later, I can fill it up, haha, bye ~)