JavaScript中的promise(二)

简介

上一节, 咱们介绍了js中promise的静态方法和实例方法, 以及使用. 本节将手写一个符合promiseA+规范的promise。javascript

  • 手写一个Promise
  • 使用promises-aplus-tests来检测是否符合promiseA+规范

先搭建一个Promise的架子.

新建一个xpromise.js的文件, 建一个class,取名为XPromise. 把静态方法和实例方法的签名加上.java

代码以下:git

class XPromise {
  constructor(cb) {
  }

  then(resolve, reject) {
  }

  catch(reject) {
  }

  finally(cb) {
  }
  
  static resolve(val) {
  }

  static reject(reason) {
  }

  static all(promises) {
  }

  static race(promises) {
  }

  static allSettled(promises) {
  }

  static deferred() {
  }
}
复制代码

接下来, 咱们实现每个方法.github

实现构造函数.

描述: 咱们先思考一下, Promise构造器里应该要作什么.npm

  • 使用Promise时, 咱们要传入一个回调, 回调里提供两个参数. resolve和reject. 如:
new Promise((resolve, reject) => {...})
复制代码
  • Promise是有状态的. pending, fulfilled和rejected三种
  • 保存了成功回调和失败回调的返回值.
  • then能够链式使用,成功回调和时候的回调应该是一个数组.

咱们就来实现以上功能, 代码以下:数组

// 定义promise的状态, 方便后面使用.
const s = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected'
};

constructor(cb) {
    // 设置初始状态为pending.
    this.status = s.pending;
    
    // 存储成功后的值.
    this.value = null;
    
    // 存储失败的缘由.
    this.reason = null;
    
    // 执行成功的回调搜集.
    this.resolveCallbacks = [];
    
    // 执行失败的回调搜集
    this.rejectCallbacks = [];
    
    const resolve = val => {
      // 若是状态是pending, 就更改.
      if (this.status === s.pending) {
        this.status = s.fulfilled;
        this.value = val;
        this.resolveCallbacks.forEach(cb => cb())
      }
    };
    
    const reject = reason => {
      if (this.status === s.pending) {
        this.status = s.rejected;
        this.reason = reason;
        this.rejectCallbacks.forEach(cb => cb());
      }
    };
    
    cb(resolve, reject);
}
复制代码

实现实例的then方法.

描述: 先思考一下, then方法, 要作哪些事情promise

  • then方法的使用. 接收两个参数, 一个是成功时的回调, 另外一个是失败是的回调. 但参数也能够不是函数. 是一个普通的对象. 好比:
// then的使用
xx.then(result => {}, error => {})

xx.then('test', 'error occurrs')
复制代码
  • 返回的仍是一个Promise. 能够链式调用.
  • 执行then时, promise的状态多是pending, fulfilled,rejected.
  • then的执行是一个异步. 须要想办法模拟.

代码以下:bash

then(resolve, reject) {
    // 对参数作检查.
    // 可能的调用: .then('success', 'error')
    // 若是不是一个方法, 就使用默认的函数.进行值传递便可.
    const onResolved = typeof resolve === 'function' ? resolve : val => val;
    const onRejected = typeof reject === 'function' ? reject : err => {throw err};
    
    // 返回的是另外一个promise对象.
    const newPromise = new XPromise((resolve, reject) => {
      // 若是调用then的时候promise的状态已经变为完成.
      // 那么调用成功的回调, 并传递参数.
      if (this.status === s.fulfilled) {
        // 使用setTimeout来模拟异步.
        setTimeout(() => {
          // 若是执行回调时, 发生异常.
          // 那么就将异常做为promise失败的缘由.
          try {
            let result = onResolved(this.value);
    
            // 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
            resolvePromise(newPromise, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
    
      if (this.status === s.rejected) {
        // 使用setTimeout来模拟异步.
        setTimeout(() => {
          // 若是执行回调时, 发生异常.
          // 那么就将异常做为promise失败的缘由.
          try {
            let result = onRejected(this.reason);
    
            // 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
            resolvePromise(newPromise, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
    
      // 若是调用then时状态仍是pending. 说明promise执行
      // 内部的resolve或reject是异步的. 须要把then中的成功回调和失败回调
      // 先存储起来, 等等promise的状态改成成功或失败的时候再执行.
      if (this.status === s.pending) {
        this.resolveCallbacks.push(() => {
          setTimeout(() => {
            try {
              const result = onResolved(this.value);
              resolvePromise(newPromise, result, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0)
        });
    
        this.rejectCallbacks.push(() => {
          setTimeout(() => {
            try {
              const result = onRejected(this.reason);
              resolvePromise(newPromise, result, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });
    
    return newPromise;
}

/** * 处理then中的返回结果. 方便链式调用. * @param {*} promise * @param {*} result * @param {*} resolve * @param {*} reject * @returns {XPromise} 返回一个promise. */
const resolvePromise = (promise, result, resolve, reject) => {
  // 参数判断.
  // 判断result和promise是否为相同的引用.防止循环引用.
  if (promise === result) {
    return reject(new TypeError('循环引用了.'));
  }

  // 用来记录promise的状态是否改变过. 
  // 一旦改变就不能再次更改为其余的状态.
  let isCalled;

  if(result !== null && 
    (typeof result === 'object' || typeof result === 'function')){
      try {
        const then = result.then;
        if(typeof then === 'function'){
          then.call(result, newResult => {
            if(isCalled)return;
            isCalled = true;

            // 这个newResult可能依旧是一个promise对象, 因此要递归调用.
            resolvePromise(promise, newResult, resolve, reject);
          }, error => {
            if(isCalled)return;
            isCalled = true;
            reject(error);
          });
        }else{
          resolve(result);
        }
      } catch (error) {
        if(isCalled)return;

        isCalled = true;
        reject(error);
      }
  }else{
    // 若是是一个普通值 那么就直接把result做为promise
    // 的成功的回调.
    resolve(result);
  }
};

复制代码

实现实例的catch方法

描述: catch方法相对简单,其实就是执行then函数的第二个参数.异步

catch(reject) {
    // 实际执行的是then的第二个参数.
    return this.then(null, reject);
}
复制代码

实现实例的finally方法

描述: 其实就是then在只是成功和失败的回调时都要执行.函数

finally(cb) {
    // then的resolve和reject都要执行.
    return this.then(cb, cb);
}
复制代码

实现static resolve

描述: 返回一个promise, 执行成功的回调. 代码实现以下, 有没有以为很是简单.

static resolve(val) {
    return new XPromise((resolve) => resolve(val));
}
复制代码

实现static reject

描述: 返回一个promise, 执行失败的回调

static reject(reason) {
    return new XPromise((resolve, reject) => reject(reason));
}
复制代码

实现static all

描述: 返回一个promise:

  • 若是参数中全部都执行成功,那么此实例执行成功的回调
  • 若是参数中有一个执行失败, 那么此实例当即执行失败的回调. 失败的缘由是第一个promise失败的结果.
static all(promises) {
    return new XPromise((resolve, reject) => {
      const arr = [];
    
      promises.forEach((p, i) => {
        p.then(data => {
          arr[i] = data;
        }, reject);
      });
    
      resolve(arr);
    });
}
复制代码

实现static race

描述: 返回一个promise,一旦参数中某个promise执行成功或失败, 就当即执行对于的回调.

static race(promises) {
    return new XPromise((resolve, reject) => {
      promises.forEach((p) => {
        // resolve后,状态不能改变
        p.then(resolve, reject);
      });
    });
}
复制代码

实现static allSettled

描述: 返回一个promise对象, 给定的全部promise都被成功解析或被失败解析,而且每一个对象都描述每一个promise的结果.

static allSettled(promises) {
    return new XPromise((resolve, reject) => {
      const arr = [];
    
      promises.forEach((p, i) => {
        p.then(data => {
          arr[i] = data;
        }, reason => {
          arr[i] = reason;
        });
      });
    
      // 等待全部都执行完成后, 再返回.
      resolve(arr);
    });
}
复制代码

实现static deferred

描述: 返回一个普通对象, 里面包含一个promise.

static deferred() {
    const defer = {}
    defer.promise = new XPromise((resolve, reject) => {
      defer.resolve = resolve
      defer.reject = reject
    })
    return defer
}
复制代码

到此, 一个Promise就实现了. 完整的代码以下:

const s = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected'
};

/** * 出来then中的返回结果. 方便链式调用. * @param {*} promise * @param {*} result * @param {*} resolve * @param {*} reject * @returns {XPromise} 返回一个promise. */
const resolvePromise = (promise, result, resolve, reject) => {
  // 参数判断.
  // 判断result和promise是否为相同的引用.防止循环引用.
  if (promise === result) {
    return reject(new TypeError('循环引用了.'));
  }

  // 用来记录promise的状态是否改变过. 
  // 一旦改变就不能再次更改为其余的状态.
  let isCalled;

  if(result !== null && 
    (typeof result === 'object' || typeof result === 'function')){
      try {
        const then = result.then;
        if(typeof then === 'function'){
          then.call(result, newResult => {
            if(isCalled)return;
            isCalled = true;

            // 这个newResult可能依旧是一个promise对象, 因此要递归调用.
            resolvePromise(promise, newResult, resolve, reject);
          }, error => {
            if(isCalled)return;
            isCalled = true;
            reject(error);
          });
        }else{
          resolve(result);
        }
      } catch (error) {
        if(isCalled)return;

        isCalled = true;
        reject(error);
      }
  }else{
    // 若是是一个普通值 那么就直接把result做为promise
    // 的成功的回调.
    resolve(result);
  }
};

class XPromise {
  static resolve(val) {
    return new XPromise((resolve) => resolve(val));
  }

  static reject(reason) {
    return new XPromise((resolve, reject) => reject(reason));
  }

  /** * - 若是参数中全部都执行成功,那么此实例执行成功的回调 * - 若是参数中有一个执行失败, 那么此实例当即执行失败的回调. 失败的缘由是第一个promise失败的结果. * @param {Array} promises Promise集合. * @returns {XPromise} 返回一个promise. */
  static all(promises) {
    return new XPromise((resolve, reject) => {
      const arr = [];

      promises.forEach((p, i) => {
        p.then(data => {
          arr[i] = data;
        }, reject);
      });

      resolve(arr);
    });
  }

  static race(promises) {
    return new XPromise((resolve, reject) => {
      promises.forEach((p) => {
        // resolve后,状态不能改变
        p.then(resolve, reject);
      });
    });
  }

  static allSettled(promises) {
    return new XPromise((resolve, reject) => {
      const arr = [];

      promises.forEach((p, i) => {
        p.then(data => {
          arr[i] = data;
        }, reason => {
          arr[i] = reason;
        });
      });

      // 等待全部都执行完成后, 再返回.
      resolve(arr);
    });
  }

  static deferred() {
    const defer = {}
    defer.promise = new XPromise((resolve, reject) => {
      defer.resolve = resolve
      defer.reject = reject
    })
    return defer
  }

  constructor(cb) {
    // 设置初始状态为pending.
    this.status = s.pending;

    // 存储成功后的值.
    this.value = null;

    // 存储失败的缘由.
    this.reason = null;

    // 执行成功的回调搜集.
    this.resolveCallbacks = [];

    // 执行失败的回调搜集
    this.rejectCallbacks = [];

    const resolve = val => {
      // 若是状态是pending, 就更改.
      if (this.status === s.pending) {
        this.status = s.fulfilled;
        this.value = val;
        this.resolveCallbacks.forEach(cb => cb())
      }
    };

    const reject = reason => {
      if (this.status === s.pending) {
        this.status = s.rejected;
        this.reason = reason;
        this.rejectCallbacks.forEach(cb => cb());
      }
    };

    cb(resolve, reject);
  }

  then(resolve, reject) {
    // 对参数作检查.
    // 可能的调用: .then('success', 'error')
    // 若是不是一个方法, 就使用默认的函数.进行值传递便可.
    const onResolved = typeof resolve === 'function' ? resolve : val => val;
    const onRejected = typeof reject === 'function' ? reject : err => {throw err};

    // 返回的是另外一个promise对象.
    const newPromise = new XPromise((resolve, reject) => {
      // 若是调用then的时候promise的状态已经变为完成.
      // 那么调用成功的回调, 并传递参数.
      if (this.status === s.fulfilled) {
        // 使用setTimeout来模拟异步.
        setTimeout(() => {
          // 若是执行回调时, 发生异常.
          // 那么就将异常做为promise失败的缘由.
          try {
            let result = onResolved(this.value);

            // 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
            resolvePromise(newPromise, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      if (this.status === s.rejected) {
        // 使用setTimeout来模拟异步.
        setTimeout(() => {
          // 若是执行回调时, 发生异常.
          // 那么就将异常做为promise失败的缘由.
          try {
            let result = onRejected(this.reason);

            // 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
            resolvePromise(newPromise, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      // 若是调用then时状态仍是pending. 说明promise执行
      // 内部的resolve或reject是异步的. 须要把then中的成功回调和失败回调
      // 先存储起来, 等等promise的状态改成成功或失败的时候再执行.
      if (this.status === s.pending) {
        this.resolveCallbacks.push(() => {
          setTimeout(() => {
            try {
              const result = onResolved(this.value);
              resolvePromise(newPromise, result, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0)
        });

        this.rejectCallbacks.push(() => {
          setTimeout(() => {
            try {
              const result = onRejected(this.reason);
              resolvePromise(newPromise, result, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });

    return newPromise;
  }

  catch(reject) {
    // 实际执行的是then的第二个参数.
    return this.then(null, reject);
  }

  finally(cb) {
    // then的resolve和reject都要执行.
    return this.then(cb, cb);
  }
}

module.exports = XPromise;
复制代码

工具测试一下, 咱们的Promise是否符合promiseA+规范

  • 安装promises-aplus-tests
npm install -D promises-aplus-tests 
复制代码
  • 执行命令
npx promises-aplus-tests ./xpromise.js
复制代码

测试结果以下:872个cases, 所有经过测试.

测试结果

demo

代码

相关文章
相关标签/搜索