最开始是在一个微信公众号上看到一篇文章,有对于手写Promise的局部内容,感觉很离奇,也是个挑战,遂本人也想尝试下,区别就是人家是一个文章列举的的罕用手写JS汇合,Promise只是其中一小块,我要独自把它拎进去讲讲,哈哈

公众号文章

文章地址

基本概念

1.Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更正当和更弱小。它由社区最早提出和实现,ES6 将其写进了语言规范,对立了用法,原生提供了Promise对象。

所谓Promise,简略说就是一个容器,外面保留着某个将来才会完结的事件(通常是一个异步操作)的后果。从语法上说,Promise 是一个对象,从它能够获取异步操作的音讯。Promise 提供对立的 API,各种异步操作都能够用同样的办法进行解决。

以上出自阮一峰的ECMAScript 6 入门

2.Promises/A+

Promises/A+ 又是啥?上面是来自Promises/A+官网的一句话

An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.

翻译成人话就是说 Promises/A+JavaScript Promise 的凋谢规范,Promise的实现都要遵循这个最根本的规范,咱们平时熟知的 ES6 Promise 就是完全符合 Promises/A+ 标准的,然而它们又不完全相同, ES6 Promise 上补充了实例上的catchfinally,静态方法allresolve以及reject等等

3.Promises/A+ 讲些啥?

最开始我是想把Promises/A+的规定全副列举进去的,起初想了一下,如同没啥必要,又臭又长。。。大家有趣味的能够本人的去看一下,我感觉英文原文次要参考一下,用谷歌翻译也能大抵根本都翻译精确,但还是有些许翻译的不到位,这里就举荐去看他人翻译的现成的了,我这边找的一个他人的翻译,集体感觉翻译的还听好的,还有相应的正文(Promises/A+翻译)

手写Promise

Promises/A+的规定熟读几遍后,就能够开始本人尝试手写Promise,我这边先放出我本人的手写实现,是用class写的,我是感觉用class是比拟精炼的,容易了解,当然你也能够用构造函数、IIFE啥的,都能够

手写Promise代码
const CustomPromise = class  {  // 定义一个动态的全副状态的map  static STATUS_MAP = {    Pending: 'Pending',    Fulfilled: 'Fulfilled',    Rejected: 'Rejected',  }  // Promise的状态  status = CustomPromise.STATUS_MAP.Pending  // then办法传入的onfulfilled函数组成的列表  onfulfilled = []  // then办法传入的onrejected函数组成的列表  onrejected = []    result = undefined  reason = undefined  // then办法返回的Promise的参数executor的回调函数resolve组成的列表  resolve = []  // then办法返回的Promise的参数executor的回调函数reject组成的列表  reject = []  // then办法返回的Promise列表  promises = []  /**   * 构造函数   * @param {function} executor Promise执行器   * @returns   */  constructor (executor) {    if (typeof executor === 'undefined' || typeof executor !== 'function') {      throw new TypeError('CustomPromise resolver is not a function')    }    /**     * 设置胜利的result以及程序执行onfulfilled函数     * @param {*} result      */    const setResult = (result) => {      this.result = result      this.status = CustomPromise.STATUS_MAP.Fulfilled      if (this.onfulfilled.length > 0) {        this.onfulfilled.forEach((onfulfilled_item, index) => {          this.excuteOnfulfilled(onfulfilled_item, index, this.result)        })      }    }    /**     * 设置失败的reason以及程序执行onrejected函数     * @param {*} result      */    const setReason= (reason) => {      this.reason = reason      this.status = CustomPromise.STATUS_MAP.Rejected      if (this.onrejected.length > 0) {        this.onrejected.forEach((onrejected_item, index) => {          this.excuteOnrejected(onrejected_item, index, this.reason)        })      }    }    try {      const resolve = (result) => {        if (this.status === CustomPromise.STATUS_MAP.Pending) { // Promise外部状态具备凝固成果,一但确定了就不再发生变化          if (result !== null && (typeof result === 'function' || typeof result === 'object')) {            let called = false            try {              const { then } = result // resolve办法能够承受一个thenable对象              if (typeof then === 'function') {                const then_ = then.bind(result)                then_(res => {                  if (called) return // 确保thenable对象then办法的resolvePromise回调函数只执行一次                  called = true                  setResult(res)                }, err => {                  if (called) return                  called = true                  setReason(err)                })              } else {                setResult(result)              }            } catch (error) {              if (called) return              setReason(error)            }          } else {            setResult(result)          }        }      }      const reject = (reason) => {        if (this.status === CustomPromise.STATUS_MAP.Pending) {          setReason(reason)        }      }      const executor_ = executor.bind(null, resolve, reject) // 为执行器绑定参数      executor_() // 执行器执行(同步)    } catch (e) {      if (this.status === CustomPromise.STATUS_MAP.Fulfilled || this.status === CustomPromise.STATUS_MAP.Rejected) return      setReason(e)    }  }  /**   * then办法   * @param {function} onfulfilled    * @param {function} onrejected    * @returns    */  then (onfulfilled, onrejected) {    this.onfulfilled.push(onfulfilled)    if (this.status === CustomPromise.STATUS_MAP.Fulfilled) { // Promise对象在状态凝固之后依然是能够调用then办法的      this.onfulfilled.forEach((item, index) => {        if (item === onfulfilled) {          this.excuteOnfulfilled(item, index, this.result)        }      })    }    this.onrejected.push(onrejected)    if (this.status === CustomPromise.STATUS_MAP.Rejected) {      this.onrejected.forEach((item, index) => {        if (item === onrejected) {          this.excuteOnrejected(item, index, this.reason)        }      })    }    const customPromise = new CustomPromise((resolve, reject) => {      this.resolve.push(resolve)      this.reject.push(reject)    })    this.promises.push(customPromise)    return customPromise // then办法返回新的Promise对象  }  /**   * 执行onfulfilled函数   * @param {function} onfulfilled    * @param {number} index    * @param {*} result    */  excuteOnfulfilled (onfulfilled, index, result) {    if (typeof onfulfilled === 'function') {      setTimeout(() => {        let x = null        try {          x = onfulfilled(result)        } catch (error) {          this.reject[index](error)        }        if (x === this.promises[index]) {          this.reject[index](new TypeError('[onFulfilled] return the same value with [then] function'))        }        this.resolutionProcedure(x, this.promises[index], this.resolve[index], this.reject[index])      }, 0)    } else {      if (this.status === CustomPromise.STATUS_MAP.Fulfilled) {        setTimeout(() => {          this.resolve[index](result)        }, 0)      }    }  }  /**   * 执行onrejected函数   * @param {function} onrejected    * @param {number} index    * @param {*} reason    */  excuteOnrejected (onrejected, index, reason) {    if (typeof onrejected === 'function') {      setTimeout(() => {        let x = null        try {          x = onrejected(reason)        } catch (error) {          this.reject[index](error)        }        if (x === this.promises[index]) {          this.reject[index](new TypeError('[onrejected] return the same value with [then] function'))        }        this.resolutionProcedure(x, this.promises[index], this.resolve[index], this.reject[index])      }, 0)    } else {      if (this.status === CustomPromise.STATUS_MAP.Rejected) {        setTimeout(() => {          this.reject[index](reason)        }, 0)      }    }  }  /**   * Promise 解决过程(重点)   * @param {*} x then办法回调函数resolvePromise执行后返回的值   * @param {CustomPromise} promise then办法返回的Promise   * @param {function} resolve then办法返回的Promise的参数executor的回调函数resolve   * @param {function} reject then办法返回的Promise的参数executor的回调函数reject   * @returns    */  resolutionProcedure (x, promise, resolve, reject) {    if (x instanceof CustomPromise) {      x.then(res => {        resolve(res)      }, err => {        reject(err)      })    } else if (x !== null && (typeof x === 'function' || typeof x === 'object')) {      let called = false      try {        const { then } = x // then办法回调函数resolvePromise执行后返回的值是一个thenable对象,执行then办法        if (typeof then === 'function') {          const then_ = then.bind(x)          const resolvePromise = y => {            if (called) return // 确保resolvePromise只执行一次            called = true            // then办法回调函数resolvePromise执行后返回的值是一个thenable对象,执行then办法后,如果then办法的resolvePromise参数被回调            // 对resolvePromise参加回调的参数y继续执行Promise 解决过程,也就是调用resolutionProcedure办法            this.resolutionProcedure(y, promise, resolve, reject)          }          const rejectPromise = r => {            if (called) return            called = true            reject(r)          }          then_(resolvePromise, rejectPromise)        } else {          resolve(x)        }      } catch (error) {        if (called) return        reject(error)      }    } else {      resolve(x)    }  }  /**   * 动态的resolved办法,返回一个曾经胜利的Promise   * @param {*} result    * @returns    */  static resolved (result) {    return new CustomPromise((resolve, reject) => {      if (result !== null && (typeof result === 'function' || typeof result === 'object')) {        let called = false        try {          const { then } = result          if (typeof then === 'function') {            const then_ = then.bind(result)            then_(res => {              if (called) return              called = true              resolve(res)            }, err => {              called = true              reject(err)            })                      } else {            resolve(result)          }        } catch (error) {          if (called) return          reject(error)        }              } else {        resolve(result)      }    })  }  /**   * 动态的rejected办法,返回一个曾经失败的Promise   * @param {*} result    * @returns    */  static rejected (reason) {    return new CustomPromise((resolve, reject) => {      reject(reason)    })  }  /**   *    * @returns 测试用   */  static deferred () {    const result = {};    result.promise = new CustomPromise(function(resolve, reject) {      result.resolve = resolve;      result.reject = reject;    });    return result;  }}module.exports = CustomPromise;

手写Promise注意事项

先列出一个典型的应用Promise的规范代码,上面有些术语会已这个为准

const p = new Promise((resolve, reject) => {  if (xxx) {    resolve()  } else {    reject(new TypeError('error'))  }})// thenable对象const thenable = (val) => {  return {    then: (resolvePromise, rejectPromise) => {      // balabala      rejectPromise(val)    }  }}const onFulfilled = (res) => {  return x}const onRejected = (err) => {}const p1 = p.then(onFulfilled, onRejected)

这里列几个我感觉在手写Promise很容易疏忽的点

  • Promise外部状态具备凝固成果,一但确定了就不再发生变化
  • Promise的构造函数是同步执行的
  • Promise外部的resolve执行是也是同步的,然而Promise在被应用时,resolve可能是被同步调用也可能是被异步调用,这个要留神。resolve如果是被同步调用的话,then办法执行的时候就要立刻执行onFulfilled以及onRejected了,resolve如果是被异步调用的话,then办法会先执行,须要把onFulfilled以及onRejected暂存起来,等到resolve被调用的时候再执行。resolve能够传入一个thenable对象,如果是thenable对象,须要执行如上面所示代码的操作(调用它的then办法)
  • onFulfilled以及onRejectedPromise外部须要被异步调用(这里权且先间接了解为异步就能够,深刻的讲还波及宏工作微工作,有趣味的能够去理解下,这里我是间接应用setTimeout实现异步的,是能够顺利通过测试的
  • then能够被屡次调用(p.then();p.then();),也能够链式调用(p.then().then();),每次then办法返回的都是一个新的Promise,所以Promise外部设计保留onFulfilled以及onRejected的数据结构是数组
  • 非常简单哔哔一下Promise的解决过程:then办法返回一个Promisep1,在返回then的这个Promise的时候,咱们把结构函数参数executor的回调函数resolve以及reject暂存起来。onFulfilled如果返回一个thenable对象(就是下面那个x)(如果不是thenable对象间接resolve(x)),对这个thenable对象执行如上面所示代码的操作(调用它的then办法)。如果resolvePromise被调用了,参数咱们示意为y,如果ythenable对象,继续执行上面的操作(如果不是间接resolve(y)),就这样始终递归上来,直到遇到y不是thenable对象(这只是我本人的简略了解,如果示意看迷糊了还请以Promises/A+原文为准,尽管我感觉那个看了可能会更迷糊。。。)
  • 须要部署三个静态方法,动态的resolved办法(也能传入thenable对象),返回一个曾经胜利的Promise;动态的rejected办法,返回一个曾经失败的Promise,动态的deferred办法,返回一个对象蕴含(一个Promise对象,新建这个Promise对象时结构函数参数executor的回调函数resolve以及reject)。resolved办法以及rejected办法不强制要求部署
  • 重点:结构函数参数executor的回调函数resolve以及动态的resolved办法都能承受一个thenable对象作为参数,须要对这个thenable对象执行如上面所示代码的操作(调用它的then办法),这是Promises/A+标准上没有细说的,开始就是疏忽了这个,导致测试没法顺利进行上来
const resolvePromise = (y) => {  // balabala}const rejectPromise = (err) => {  // balabala}thenable.then(resolvePromise, rejectPromise)

利用promises-tests对手写Promise进线测试

测试步骤很简略

  • 装置依赖
npm install promises-aplus-tests --save-dev
  • package.json退出脚本
"test": "promises-aplus-tests <你的手写Promise JS门路>"
  • 控制台输出
npm run test
  • 期待后果。。。

释怀一开始预计后果都不会太难看,就像我这样。。。

到最初

排错指南

可能有人苦苦排查,一遍又一遍的查看本人的代码,但每次测试还总是那几个项报错,很是苦恼,想当初我就是啊,这里我就要倡议你去看一下promises-tests的源码了(释怀源码很小),正所谓知己知彼百战不殆啊,你只有晓得了考试内容才晓得怎么通过考试不是?(如同这个比喻怪怪的)

promises-tests仓库地址

我的项目先克隆下来再关上,看看入口是啥

"bin": "lib/cli.js",,

咱们关上lib/cli.js

发现其主函数就在lib/programmaticRunner.js

promises-tests用的是Mocha进行的测试,测试文件在lib/tests下,他会读取测试文件顺次进行测试

你能够依据测试的谬误提醒定位本人失败的测试用例

我是大部分测试都栽在了2.3.3.3,咱们关上2.3.3看看,发现其蕴含一个主的测试用例,其中又有三个测试用例

再顺次关上,直到2.3.3.3

抽取其中的次要测试代码

  • PromiseTest.js
import thenables from './thenables';import CustomPromise from '../promise/CustomPromise';const resolved = CustomPromise.resolved;const rejected = CustomPromise.rejected;const sentinel = { sentinel: "sentinel" };const dummy = { dummy: "dummy" };export default function testMain () {  function yFactory() {    return thenables.fulfilled['an already-fulfilled promise'](thenables.fulfilled['an asynchronously-fulfilled custom thenable'](sentinel));  }    function xFactory() {    return {      then: function (resolvePromise) {        const yFactory_ = yFactory()        resolvePromise(yFactory_);      }    };  }    const promise = resolved(dummy).then(function onBasePromiseFulfilled() {    const xFactory_ = xFactory()    return xFactory_;  });    const test = function (promise) {    promise.then(function onPromiseFulfilled(value) {      console.log('最终:', value);    })  }  test(promise)}
  • thenables.js
import CustomPromise from './CustomPromise';var resolved = CustomPromise.resolved;var rejected = CustomPromise.rejected;var deferred = CustomPromise.deferred;var other = { other: "other" }; // a value we don't want to be strict equal toconst fulfilled = {    "a synchronously-fulfilled custom thenable": function (value) {        return {            then: function (onFulfilled) {                onFulfilled(value);            }        };    },    // 略};const rejected_ = {    "a synchronously-rejected custom thenable": function (reason) {        return {            then: function (onFulfilled, onRejected) {                onRejected(reason);            }        };    },    // 略};export default {    fulfilled: fulfilled,    rejected: rejected_}

fulfilled状况下能输入{ sentinel: "sentinel" }就代表测试通过了,而后就依据这段小型测试代码去一直调试本人的程序,在一直的改良下,就能在一次偶尔的测试中失去872 passing了!

手写Promise的意义

  • 首先就是能通过手写PromisePromise能有更加深刻的了解,就比方我之前是齐全就不知道onFulfilled返回thenable的后续操作的,当然这个实现还很简陋,比方ES6 Promise 实例上的catchfinally,静态方法allany以及race等等都还没实现,后续能够加上
  • 其次就是当初的面试动不动就造航母,不学点货色不行啊

下期预报

如果上线顺利应该会推下我的小程序,欢送大家试用