directory

  1. What problem did Promise solve
  2. The use of the Promise
  3. Promise/A + specification
  4. Implement A Promise that conforms to the Promise/A+ specification
  5. Implement promises based on observation patterns
  6. [Full version] – Added then methods to implement chain calls

1. What problems did Promise solve

Promises allow us to solve the problem of “callback hell” by chain-calling, especially in asynchronous processes, and Promise to keep code clean and readable. This paper mainly interprets the Promise/A+ specification, and implements A Promise on the basis of this specification.

The use of Promise

1) Create a Promise instance

var p=new Promise(function(resolve,reject){
    setTimeout(function(){
       resolve("success")},1000);
    console.log("Create a new Promise");
})
p.then(function(x){
  console.log(x)
})

/ / output:
// Create a new promise
// success

Copy the code

Here is an example of a promise, and the output reads, “Create a Promise”, and after a 1000ms delay, “success” is printed.

As you can see from the examples above, promises are convenient for handling asynchronous operations. In addition, promise can be invoked in chained form:

var p=new Promise(function(resolve,reject){resolve()}); p.then(...) .then(...) .then(...)Copy the code

In addition to the then method, Promise also provides promise. resolve, promise. all, promise. race and so on.

Iii. Promise/A+ specification

The Promise/A+ specification expands on earlier Promise/A proposal proposals, so let’s take A look at the Promise/A+ specification.

1) terms

(1) a “promise” is an object or function that has a then method

(2) “thenable” is an object or function that defines the then method

(3) “value” is the value when the promise state succeeds

(4) “reason” is the value when the promise state fails

The purpose of specifying terms is to keep the code normative as we implement promises ourselves (you can skip this section too).

2) requirements

(1) A promise must have three states: pending, fulfilled(resolved), and when it is in the pending state, it can be moved to the fulfilled(resolved) or rejected state. This will be a pity (resolved) state or the rejected state.

A promise is a promise, which means that once the state of a promise changes, it is forever irreversible

(2) A promise must have a THEN method that takes two arguments:

promise.then(onFulfilled,onRejected)
Copy the code

Where onFulfilled method represents the method executed when the state is fulfilled from pending — >fulfilled(resolved), and onRejected represents the method executed when the state is fulfilled from pending — > Fulfilled (resolved).

(3) In order to implement chained calls, the then method must return a promise

promise2=promise1.then(onFulfilled,onRejected)
Copy the code

Implement A Promise that conforms to the Promise/A+ specification

Now that we’ve read the Promise/A+ specification, let’s see how to implement A Promise,

Start by constructing a myPromise function, which should remain the same as in the specification for all variables and function names.

1) Initial version myPromise

Also, you need to define the then method of the chain call on the myPromise prototype:

A state change occurs in myPromise, and then the corresponding THEN method can perform different actions depending on the state.

function myPromise(constructor){
    let self=this;
    console.log('self0:',self, 'this0:'.this); // This refers to the instance object generated by new.
    self.status="pending" // Define the initial state before the state changes
    self.value=undefined;// Determine the resolved state
    self.reason=undefined;// Define the state of the rejected state
    function resolve(value){
        console.log('self:',self, 'this:'.this); // This points to window. Because this function is called as a parameter belonging to the instance object.
        // Two ==="pending" guarantees that state changes are irreversible
       if(self.status==="pending"){
          self.value=value;
          self.status="resolved"; }}function reject(reason){
        // Two ==="pending" guarantees that state changes are irreversible
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected"; }}// Catch a construction exception
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);// Reject (e)}}; myPromise.prototype.then=function(onFullfilled,onRejected){
   let self=this;
   console.log('then the self:,self, 'then this:.this); Then ((res)=>{},(rej)=>{})), there is no need to save this.
   switch(self.status){
      case "resolved":
        onFullfilled(self.value); //
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:}}var p=new myPromise(function(resolve,reject){resolve(111)});
p.then(function(x){console.log(x)})
Copy the code

But here myPromise cannot handle asynchronous resolve. For example:

var p=new myPromise(function(resolve,reject){setTimeout(function(){resolve(1)},1000)});

p.then(function(x){console.log(x)})
/ / no output
Copy the code

Think about:

  • Why can't myPromise handle asynchronous resolve?

Implement Promise based on observation pattern

To handle asynchronous resolve, we change the myPromise definition to use two arrays onFullfilledArray and onRejectedArray to hold asynchronous methods. Executes the methods in the array once when the state changes.


function myPromise(constructor){
    let self=this;
    self.status="pending" // Define the initial state before the state changes
    self.value=undefined;// Determine the resolved state
    self.reason=undefined;// Define the state of the rejected state
    self.onFullfilledArray=[]; // Store the successful callback function
    self.onRejectedArray=[];// Store the failed callback function
    function resolve(value){
       if(self.status==="pending") {// Ensure that the state change is irreversible
          self.value=value;
          self.status="resolved";
          // Change the state to success while pending and pass the data.
          // And iterate over the array of successful callbacks
          console.log('Resolved success callback array in constructor:', self.onFullfilledArray);
          self.onFullfilledArray.forEach(function(f){
                f(self.value);
                // If the state changes from Pending to Resolved,
                // Then iterate over the asynchronous method in the execution}); }}function reject(reason){
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected";
          self.onRejectedArray.forEach(function(f){
              f(self.reason);
             // If the status changes from Pending to Rejected,
             // Then iterate over the asynchronous method in the execution}}})// Catch a construction exception
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);
    }
}


myPromise.prototype.then=function(onFullfilled,onRejected){
   let self=this;
   switch(self.status){
      case "pending": // How to push the success and failure callback functions into the corresponding array while pending. (Ensures that the state change is irreversible)
        console.log(This pending in 'then 'and value above it :', self);
        self.onFullfilledArray.push(function(){
             onFullfilled(self.value)
        });
        self.onRejectedArray.push(function(){
             onRejected(self.reason)
        });
        console.log(Self. onFullfilledArray Success array:, self.onFullfilledArray);
      case "resolved": // Pass the parameter to the passed function and call the passed function
        console.log(This :' resolved in 'then, self);
        onFullfilled(self.value);
        break;
      case "rejected":
        console.log(This :' rejected 'in 'then', self);
        onRejected(self.reason);
        break;
      default:}}var p=new myPromise(function(resolve,reject){setTimeout(function(){resolve(100)},1000)});

p.then(function(x){console.log(x)})
Copy the code

This way, by using two arrays and starting execution after a state change, can handle the problem of asynchronous resolve not being called. This version of myPromise can handle all asynchrony, so is that complete?

No, the biggest feature of the Promise/A+ specification is the chain call, which means that the then method should return A Promise.

Full version – Added then methods to implement chain calls

To realize the chain call through then method, that is to say, then method needs to return a primise every time it is called, and at the same time, add an error handling part in the constructor that returns promise. Let’s transform then method

function myPromise(constructor){
    let self=this;
    self.status="pending" // Define the initial state before the state changes
    self.value=undefined;// Determine the resolved state
    self.reason=undefined;// Define the state of the rejected state
    self.onFullfilledArray=[]; // Store the successful callback function
    self.onRejectedArray=[];// Store the failed callback function
    function resolve(value){
       if(self.status==="pending") {// Ensure that the state change is irreversible
          self.value=value;
          self.status="resolved";
          // Change the state to success while pending and pass the data.
          // And iterate over the array of successful callbacks
          console.log('Resolved success callback array in constructor:', self.onFullfilledArray);
          self.onFullfilledArray.forEach(function(f){
                f(self.value);
                // If the state changes from Pending to Resolved,
                // Then iterate over the asynchronous method in the execution}); }}function reject(reason){
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected";
          self.onRejectedArray.forEach(function(f){
              f(self.reason);
             // If the status changes from Pending to Rejected,
             // Then iterate over the asynchronous method in the execution}}})// Catch a construction exception
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);
    }
}

myPromise.prototype.then=function(onFullfilled,onRejected){
    let self=this;
    let promise2;
    switch(self.status){
      case "pending":
        promise2=new myPromise(function(resolve,reject){
             self.onFullfilledArray.push(function(){
                try{
                   let temple=onFullfilled(self.value);
                   resolve(temple)
                }catch(e){
                   reject(e) //error catch}}); self.onRejectedArray.push(function(){
                 try{
                   let temple=onRejected(self.reason);
                   reject(temple)
                 }catch(e){
                   reject(e)// error catch}}); })case "resolved":
        promise2=new myPromise(function(resolve,reject){
            try{
              let temple=onFullfilled(self.value);
              // Pass the method from the previous then to the next Promise state
              resolve(temple);
            }catch(e){
              reject(e);//error catch}})break;
      case "rejected":
        promise2=new myPromise(function(resolve,reject){
            try{
               let temple=onRejected(self.reason);
               // Pass the methods in then to the next Promise state
               resolve(temple);   
            }catch(e){ reject(e); }})break;
      default:}return promise2;
}

var p=new myPromise(function(resolve,reject){setTimeout(function(){resolve(111)},1000)});
p.then(function(x){console.log(x)}).then(function(){console.log("Chain call 1")}).then(function(){console.log("Chain call 2")})
/ / output

// call 1
// chain call 2
/ / 111
Copy the code

In the Promise/A+ specification, the onFullfilled () and onRejected () methods can return an object, A function, or even another Promise.

To do:

See in reference

1) The onFullfilled and onRejected methods in the then function return values

In particular, the return value of the onFullfilled and onRejected methods may be a promise.

Let’s start with the promise requirement for the return value of the onFullfilled function

I) If the onFullfilled function returns the promise itself, a type error will be thrown

If the onFullfilled function returns a different promise, execute the promise’s then function, which transfers the state of the promise to the new promise. III) If the return is a nested promsie, So we need recursion.

IV) If a non-PROMsie object or function is returned, it will choose to give that object or function directly to the new Promise.

The resolve function will be redefined as Promise/A+ : The resolvePromise function, which takes the current promise, the onFullfilled function, or the return value of the onRejected function, resolve, and reject.

reference

  • Implement A Promise that perfectly conforms to the Promise/A+ specification](github.com/fortheallli…)

conclusion

  • In the Promise constructor, if the state starts frompendingintoresolved, thenIterate over the asynchronous methods within the execution

Observer model understanding

1) Target objects

  • The target object (observed) is of the Promise constructorThe instanceThe instance stores arrays of successful and failed callbacks, hereThe store is essentially a list of observers.
self.onFullfilledArray=[{},{}];
Copy the code

So, where do these observers come from, push

Push in then is pushing an observer instance into the observer list.

// Execute at some point

When the state changes from Pending to Rejected, all the callbacks passed in to the observer object instances in the observer list are executed.

2) Observers

var p=new myPromise(function(resolve,reject){setTimeout(function(){resolve(1)},1000)});

p.then(function(x){console.log(x)})
Copy the code

P is the target object. Cb passed to the target object is the observer.

When the state of the target object changes to successful/failed, the observer callback in the corresponding successful/failed observer list is executed

2) Analysis of the composition of target object observers

  • The target objects include:Observer array.Iterate over the execution of these observer instancesThe callback function passed in.(that is, calling updates on these observer instance objects)
  • The observer instance contains:A callback function executed when a notification is received from the target object

Note: The implementation of this Pomise observer pattern does not provide the update method to be called by the target object. The update method executed when the target object is notified is used to call the callback function passed in by the observer instance