图解 Promise 实现原理(四)—— Promise 静态方法实现

本文首发于 vivo互联网技术 微信公众号
连接: https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ
做者:Morraingit

了用法,原生提供了Promise对象。更多关于 Promise 的介绍请参考阮一峰老师的 ES6入门 之 Promise 对象es6

不少同窗在学习 Promise 时,知其然殊不知其因此然,对其中的用法理解不了。本系列文章由浅入深逐步实现 Promise,并结合流程图、实例以及动画进行演示,达到深入理解 Promise 用法的目的。github

本系列文章有以下几个章节组成:设计模式

  1. 图解 Promise 实现原理(一)—— 基础实现数组

  2. 图解 Promise 实现原理(二)—— Promise 链式调用promise

  3. 图解 Promise 实现原理(三)—— Promise 原型方法实现缓存

  4. 图解 Promise 实现原理(四)—— Promise 静态方法实现bash

1、前言

上一节中,实现了 Promise 的原型方法。包括增长异常状态,catch以及 finally。截至目前,Promise 的实现以下:微信

class Promise {
  callbacks = [];
  state = 'pending';//增长状态
  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 (!cb) {//若是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';//改变状态
    this.value = value;//保存结果
    this.callbacks.forEach(callback => this._handle(callback));
  }
  _reject(error) {
    this.state = 'rejected';
    this.value = error;
    this.callbacks.forEach(callback => this._handle(callback));
  }
}复制代码
接下来再介绍一下 Promise 中静态方法的实现,譬如 Promise.resolve、Promise.reject、Promise.all 和 Promise.race。其它静态方法的实现也是相似的。

2、静态方法

一、Promise.resolve && Promise.reject

除了前文中提到的 Promise实例的原型方法外,Promise 还提供了 Promise.resolve 和Promise.reject 方法。用于将非 Promise 实例包装为 Promise 实例。例如:异步

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))复制代码
Promise.resolve 的参数不一样对应的处理也不一样,若是 Promise.resolve 的参数是一个 Promise的实例,那么 Promise.resolve 将不作任何改动,直接返回这个 Promise 实例,若是是一个基本数据类型,譬如上例中的字符串,Promise.resolve 就会新建一个 Promise 实例返回。这样当咱们不清楚拿到的对象究竟是不是 Promise 实例时,为了保证统一的行为,Promise.resolve 就变得颇有用了。看一个例子:
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);
});复制代码
上面的场景咱们会常常碰到,为了减小请求,常常会缓存数据,咱们获取到 id 对应的名字后,存到 Id2NameMap 对象里,下次再经过 id 去请求 id 对应的 name 时先看 Id2NameMap里有没有,若是有就直接返回对应的 name,若是没有就发起异步请求,获取到后放到 Id2NameMap 中去。

其实上面的代码是有问题的,若是命中 Id2NameMap 里的值,getNameById 返回的结果就是 name,而不是 Promise 实例。此时 getNameById(id).then 会报错。在咱们不清楚返回的是不是 Promise 实例的状况下,就可使用 Promise.resolve 进行包装:

Promise.resolve(getNameById(id)).then(name => {
  console.log(name);
});复制代码
这样一来,无论 getNameById(id) 返回的是什么,逻辑都没有问题。看下面的Demo:

demo-Promise.resolve 的源码

在实现 Promise.resolve 以前,咱们先看下它的参数分为哪些状况:

(1)参数是一个 Promise 实例

若是参数是 Promise 实例,那么 Promise.resolve 将不作任何修改、原封不动地返回这个实例。

(2)参数是一个 thenable 对象

thenable 对象指的是具备 then 方法的对象,好比下面这个对象。

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};复制代码
Promise.resolve 方法会将这个对象转为 Promise 对象,而后就当即执行 thenable 对象的 then方法。
let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};
 
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});复制代码
上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而当即执行最后那个then方法指定的回调函数,输出 42。

(3)参数不是具备 then 方法的对象,或根本就不是对象

若是参数是一个原始值,或者是一个不具备then方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。

(4)不带任务参数

Promise.resolve 方法容许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。

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 与 Promise.resolve 相似,区别在于 Promise.reject 始终返回一个状态的 rejected 的 Promise 实例,而 Promise.resolve 的参数若是是一个 Promise 实例的话,返回的是参数对应的 Promise 实例,因此状态不必定。

Promise.reject 的实现源码

二、Promise.all && Promise.race

Promise.all 接收一个 Promise 实例的数组,在全部这些 Promise 的实例都 fulfilled 后,按照 Promise 实例的顺序返回相应结果的数组。

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 的实现以下:
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 的实现源码

Promise.race 也接收一个 Promise 实例的数组,与 Promise.all不一样的是,因此返回的结果是这些 Promise 实例中最早 fulfilled 的。

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 的实现以下:
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 的实现源码

3、总结

刚开始看 Promise 源码的时候总不能很好的理解 then 和 resolve 函数的运行机理,可是若是你静下心来,反过来根据执行 Promise 时的逻辑来推演,就不难理解了。这里必定要注意的点是:Promise 里面的 then 函数仅仅是注册了后续须要执行的代码,真正的执行是在 resolve 方法里面执行的,理清了这层,再来分析源码会省力的多。

如今回顾下 Promise 的实现过程,其主要使用了设计模式中的观察者模式:

  1. 经过 Promise.prototype.then 和 Promise.prototype.catch 方法将观察者方法注册到被观察者 Promise 对象中,同时返回一个新的 Promise 对象,以即可以链式调用。

  2. 被观察者管理内部 pending、fulfilled 和 rejected 的状态转变,同时经过构造函数中传递的 resolve 和 reject 方法以主动触发状态转变和通知观察者。

本系列图文讲解的是 Promise 的思想,实现的内容并不能彻底知足 Promise/A+ 规范的全部要求。

4、参考资料

  1. 【翻译】Promises/A+规范

  2. 深刻 Promise(一)——Promise 实现详解

  3. 30分钟,让你完全明白Promise原理

更多内容敬请关注 vivo 互联网技术 微信公众号

注:转载文章请先与微信号:Labs2020 联系。

相关文章
相关标签/搜索