Graphic realization principle of promise (4) — Realization of promise static method

Time:2020-9-25

This article starts with WeChat official account of vivo Internet technology.
Link: https://mp.weixin.qq.com/s/Lp_ 5BXdpm7G29Z7zT_ S-bQ
Author: morrain

A promise object is provided natively. For more information on promise, please refer to Ruan Yifeng’s ES6 introduction promise object.

Many students are learning promise, but they don’t know why. They can’t understand the usage of promise.This series of articles from simple to deep gradually realize promise, and combined with flow chart, examples and animation to demonstrate, to achieve a profound understanding of the purpose of promise usage.

This series of articles consists of the following chapters:

  1. Schematic realization principle of promise (1) — basic realization
  2. Graphical realization principle of promise (2) — chain call of promise
  3. Graphic realization principle of promise (3) — Realization of promise prototype method
  4. Graphic realization principle of promise (4) — Realization of promise static method

1、 Preface

In the previous section, we implemented the prototype method of promise. It includes adding exception status, catch and finally. Up to now, promise has been implemented as follows:

class Promise {
  callbacks = [];
  State ='pending '; // increase state
  Value = null; // save the result
  constructor(fn) {
    fn(this._resolve.bind(this), this._reject.bind(this));
  }
  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      this._handle({
        onFulfilled: onFulfilled || null,
        onRejected: onRejected || null,
        resolve: resolve,
        reject: reject
      });
    });
  }
  catch(onError) {
    return this.then(null, onError);
  }
  finally(onDone) {
    if (typeof onDone !== 'function') return this.then();
 
    let Promise = this.constructor;
    return this.then(
      value => Promise.resolve(onDone()).then(() => value),
      reason => Promise.resolve(onDone()).then(() => { throw reason })
    );
  }
  _handle(callback) {
    if (this.state === 'pending') {
      this.callbacks.push(callback);
      return;
    }
 
    let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
 
    If (! CB) {// if nothing is passed in then
      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
      cb(this.value);
      return;
    }
 
    let ret;
 
    try {
      ret = cb(this.value);
      cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
    } catch (error) {
      ret = error;
      cb = callback.reject
    } finally {
      cb(ret);
    }
 
  }
  _resolve(value) {
 
    if (value && (typeof value === 'object' || typeof value === 'function')) {
      var then = value.then;
      if (typeof then === 'function') {
        then.call(value, this._resolve.bind(this), this._reject.bind(this));
        return;
      }
    }
 
    this.state  ='fulfilled'; // change state
    this.value  =Value; // save the result
    this.callbacks.forEach(callback => this._handle(callback));
  }
  _reject(error) {
    this.state = 'rejected';
    this.value = error;
    this.callbacks.forEach(callback => this._handle(callback));
  }
}

Next, we will introduce the implementation of static methods in promise, such as Promise.resolve , Promise.reject , Promise.all And Promise.race 。 The implementation of other static methods is similar.

2、 Static method

1、Promise.resolve && Promise.reject

In addition to the prototype approach for the promise instance mentioned earlier, promise also provides Promise.resolve and Promise.reject method. Used to wrap a non promise instance as a promise instance. For example:

Promise.resolve('foo')
//Equivalent to
new Promise(resolve => resolve('foo'))

Promise.resolve The corresponding processing is different for different parameters of Promise.resolve Is an instance of promise, then Promise.resolve This promise instance will be returned directly without any changes. If it is a basic data type, such as the string in the above example, Promise.resolve A promise instance will be created and returned. In this way, when we don’t know whether the obtained object is a promise instance or not, in order to ensure uniform behavior, Promise.resolve It becomes very useful. Take an example:

const Id2NameMap = {};
const getNameById = function (id) {
 
  if (Id2NameMap[id]) return Id2NameMap[id];
 
  return new Promise(resolve => {
    mockGetNameById(id, function (name) {
      Id2NameMap[id] = name;
      resolve(name);
    })
  });
}
getNameById(id).then(name => {
  console.log(name);
});

We often encounter the above scenario. In order to reduce the number of requests, we often cache the data. After we get the name corresponding to the ID, we save it in the id2namemap object. Next time, when we request the name corresponding to the ID through the ID, we first check whether there is one in the id2namemap. If there is, we will directly return the corresponding name. If not, we will initiate an asynchronous request and put it into id2namemap Middle.

In fact, there is a problem with the above code. If the value in id2namemap is hit, the result returned by getnamebyid is name instead of promise instance. At this time, getnamebyid (ID). Then will report an error. We can use the Promise.resolve Packaging:

Promise.resolve(getNameById(id)).then(name => {
  console.log(name);
});

In this way, no matter what getnamebyid (ID) returns, the logic is fine. Look at the demo below:

demo- Promise.resolve Source code of

In the implementation Promise.resolve Before that, let’s take a look at its parameters

(1) Parameter is a promise instance

If the parameter is a promise instance, then Promise.resolve This instance will be returned intact without any modification.

(2) The parameter is a thenable object

Thenable objects refer to objects with then methods, such as the following.

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};

Promise.resolve Method converts this object into a promise object and immediately executes the then method of the thenable object.

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};
 
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

In the above code, after the then method of thenable object is executed, the state of object P1 will change to resolved, so that the callback function specified by the last then method will be executed immediately, and output 42.

(3) The parameter is not an object with a then method, or is not an object at all

If the parameter is an original value or an object that does not have the then method, then Promise.resolve Method returns a new promise object with the state resolved.

(4) Without task parameters

Promise.resolve Method allows you to directly return a promise object in the resolved state without parameters.

static resolve(value) {
  if (value && value instanceof Promise) {
    return value;
  } else if (value && typeof value === 'object' && typeof value.then === 'function') {
    let then = value.then;
    return new Promise(resolve => {
      then(resolve);
    });
 
 
  } else if (value) {
    return new Promise(resolve => resolve(value));
  } else {
    return new Promise(resolve => resolve());
  }
}

Promise.reject And Promise.resolve The difference is that Promise.reject Always returns a rejected promise instance of the state, and Promise.resolve If the parameter of is a promise instance, the return is the promise instance corresponding to the parameter, so the status is not necessarily.

Promise.reject Implementation source code

2、Promise.all && Promise.race

Promise.all Receive an array of promise instances. After all these promise instances are fulfilled, the array of corresponding results is returned in the order of promise instances.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('p1'), 1000)
})
 
 
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('p2'), 5000)
})
 
Promise.all([p1, p2]).then(rets => {
   console.log(rets) // ['p1','p2']
})

Promise.all The implementation is as follows:

static all(promises) {
  return new Promise((resolve, reject) => {
    let fulfilledCount = 0
    const itemNum = promises.length
    const rets = Array.from({ length: itemNum })
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then(result => {
        fulfilledCount++;
        rets[index] = result;
        if (fulfilledCount === itemNum) {
          resolve(rets);
        }
      }, reason => reject(reason));
    })
  })
}

Promise.all Implementation source code

Promise.race Also receives an array of promise instances, and Promise.all The difference is, therefore, the returned result is the first fulfilled of these promise instances.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('p1'), 1000)
})
 
 
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('p2'), 5000)
})
 
Promise.race([p1, p2]).then(ret => {
   console.log(ret) // 'p1'
})

Promise.race The implementation is as follows:

static race(promises) {
  return new Promise(function (resolve, reject) {
    for (let i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then(function (value) {
        return resolve(value)
      }, function (reason) {
        return reject(reason)
      })
    }
  })
}

Promise.race Implementation source code

3、 Summary

When you first look at promise source code, you can’t understand the operation mechanism of then and resolve functions, but if you calm down and deduce according to the logic of promise implementation, it is not difficult to understand. It must be noted here that the then function in promise only registers the subsequent code to be executed, and the real execution is executed in the resolve method. It will be much more labor-saving to clarify this layer and analyze the source code.

Now let’s review the implementation of promise, which mainly uses the observer pattern in design patterns

  1. Through Promise.prototype.then And Promise.prototype.catch Method registers the observer method with the observed promise object and returns a new promise object for chaining.
  2. The observer manages the internal state transitions of pending, fulfilled and rejected, and initiatively triggers the state transition and notifies the observer through the resolve and reject methods passed in the constructor.

This series of pictures and texts explain the idea of promise, and the content can not fully meet all the requirements of promise / A + specification.

4、 References

  1. Promise / A + specification
  2. In depth promise (1) — a detailed explanation of promise implementation
  3. 30 minutes, let you thoroughly understand the promise principle

Please pay attention to more detailsVivo Internet technologyWeChat official account

Graphic realization principle of promise (4) -- Realization of promise static method

Note: please contact the wechat:Labs2020Contact.

Recommended Today

(2) start with mybatis

Prospect review In the MVP version, we have implemented a basic running mybatis. As the saying goes, everything is difficult at the beginning, then difficult in the middle. The plug-in mechanism of mybatis is the second soul besides dynamic proxy. Let’s experience the pain and happiness of this interesting soul~ The role of plug-ins In […]