In this paper, starting from vivo Internet technology WeChat public links: https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ author: Morrain

Used, native provides Promise objects. For more information about promises, please refer to Ruan Yifeng’s ES6 Introduction to Promise Objects.

Many students know the Promise but don’t know its reason and can’t understand its usage. This series of articles gradually realize Promise from simple to deep, and combined with flow charts, examples and animations to demonstrate, to achieve a deep understanding of the purpose of Promise usage.

This article series consists of the following chapters:

  1. Promise Implementation Principle (I) — Basic implementation

  2. (2) – The Promise chain call

  3. Graphic Promise implementation principle (three) – Promise prototype method implementation

  4. Schematic Promise implementation principle (four) – Promise static method implementation

One, foreword

In the previous section, you implemented the prototype approach for Promise. This includes adding exception states, catch, and finally. So far, the Promise implementation is as follows:

class Promise {
  callbacks = [];
  state = 'pending'; // Add status value = null; 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(! If cb) {/ /thenCb = 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'; 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)); }}Copy the code


Resolve, promise.Reject, Promise.all, and promise.race. The implementation of other static methods is similar.

Second, static method

1, Promise. Resolve && Promise. Reject

In addition to the prototype method of the Promise instance mentioned earlier, promises also provide the promise.resolve and Promise.reject methods. Use to wrap a non-PROMISE instance as a Promise instance. Such as:

Promise.resolve('foo'New Promise(resolve => resolve();'foo'))Copy the code


Resolve is an instance of a Promise. If the Promise. Resolve argument is an instance of a Promise, the promise. resolve instance will be returned unchanged. If it is a basic datatype, such as the string in the example above, promise.resolve will return a new Promise instance. This makes promise.resolve useful to ensure consistent behavior when it’s not clear whether the object we’re getting is a Promise instance. Look at 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); });Copy the code


In order to reduce requests, we often cache data. After obtaining the name corresponding to the ID, we store it in the Id2NameMap object. Next time we request the name corresponding to the ID through the ID, we first check whether there is Id2NameMap. If yes, return the corresponding name. If no, initiate an asynchronous request. After obtaining Id2NameMap, put it in Id2NameMap.

If a value in Id2NameMap is hit, getNameById returns the name, not the Promise instance. The getNameById(ID).then error is reported. Resolve can be wrapped with promise.resolve in cases where it is not clear whether the promised instance is being returned:

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


That way, no matter what getNameById(ID) returns, the logic is fine. Look at the following Demo:

The source code of the demo – Promise. Resolve

Resolve before implementing promise.resolve, let’s take a look at its arguments:

(1) The argument is an instance of Promise

If the argument is a Promise instance, promise.resolve will return the instance unchanged.

(2) The argument is a Thenable object

Thenable objects refer to objects that have then methods, such as this one.

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


The promise. resolve method converts this object to a Promise, and then immediately executes the thenable object’s then method.

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


As soon as the thenable object’s THEN method executes, object P1 will be in the resolved state and the callback specified by the last THEN method will execute immediately, printing 42.

(3) Arguments are not objects with then methods, or are not objects at all

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

(4) No task parameters

The Promise. Resolve method allows you to call an Resolved Promise object with no arguments.

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 {
    returnnew Promise(resolve => resolve()); }}Copy the code



Reject is similar to promise. resolve, except that promise. reject always returns a Promise instance of the state Rejected, The Promise. Resolve parameter, if it is a Promise instance, returns the corresponding Promise instance, so the state is not necessarily the same.

Promise. Reject implementation source code

2, promise.all && promise.race

Promise. All receives an array of Promise instances, and after all these Promise instances are fulfilled, the array of corresponding results will be returned according to the order of the 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']})Copy the code


Promise.all is implemented 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)); })})}Copy the code



Promise. All implementation source code

Promise. Race also receives an array of Promise instances, which is different from Promise. All, so the return result is the first pity among 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'
})Copy the code


Promise.race is implemented 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)
      })
    }
  })
}Copy the code

Promise. Race implementation source code

Third, summary

How the THEN and resolve functions work isn’t always easy to understand when you first look at the Promise source code, but if you stop and work your way back to the logic used to implement promises, it will. It’s important to note that the then function in Promise simply registers the code that needs to be executed later. The actual execution is performed in the resolve method, so it’s much easier to parse the source code.

Let’s review the implementation of the Promise, which uses the observer pattern of the design pattern:

  1. Use the promise.prototype. then and promise.prototype. catch methods to register the observer method with the observed Promise and return a new Promise so that it can be called in chain.

  2. The observer manages internal pending, DEPRESSING, and Rejected state transitions, and actively triggers state transitions 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, the implementation of the content can not fully meet all the requirements of Promise/A+ specification.

Iv. Reference materials

  1. 【 例 句 】Promises/A+ Promises

  2. In-depth Promise(a) — Promise implementation details

  3. 30 minutes to make sure you understand the Promise principle

For more content, please pay attention to vivo Internet technology wechat public account

Note: To reprint the article, please contact our wechat account: Labs2020.