学习Promise && 简易实现Promise

1,025 阅读8分钟

Promise和异步

  • Promise异步解决方案,是其他异步方案的基石
  • 异步:现在和将来之间的时间间隙
  • Promises/A+规范

Promise核心方法和辅助方法

  • Promise.prototype.then ( 核心 )
  • Promise.prototype.catch ( 辅助 )
  • Promise.resolve ( 辅助 )
  • Promise.reject ( 辅助 )
  • Promise.all ( 辅助 )
  • Promise.race ( 辅助 )
  • 阮一峰(promise)

Promise

  1. promise 状态变更后不可逆
  2. 同一个实例可以被多次 then
  3. .then方法是异步且可以被链式调用( 返回新的promise )
  4. .then方法中用户可以显示的返回 promise ,且可以多层嵌套。实例如下图

http://occeqxmsk.bkt.clouddn.com/status_-all.png

Promise中也许是最麻烦的部分1 ( 订阅发布 )

  1. new Promise 中使用了异步方法,那么当前状态就会一直处于 pending 状态,直到在异步的回调中触发 resolvereject
  2. 那么在构造函数 Promise 中,就需要把then里面的成功和失败函数,存储在私有数组中 this.onResolvedArythis.onRejectedAry
  3. then 方法为 pending 状态时,把第一个成功参数 onFulFilled 和第二个失败参数 onRejected,分别存储到私有变量 this.onResolvedArythis.onRejectedAry
  4. 当异步回调中的 resolverejcet 被触发时,分别遍历对应的数组 (onResolvdeAry, onRejectedAry) 并执行即可

Promise中也许是最麻烦的部分2 ( 递归 )

默认返回Promise和用户显示的返回Promise

  1. defaultReturnPromise.then 中默认返回新的 promise (链式调用)
  2. userReturnValue:用户可以显示的返回 promise 或其他参数
  3. 如果用户返回的是普通字符,则直接调用 defaultReturnPromiseresolve
  4. 如果用户返回的是一个 promise,则看用户返回的 promise 内部调用的 resolve 还是 reject
  5. 如果调用的是 resolve ,则拿着 resolve 的参数直接执行 defaultReturnPromiseresolve
  6. 如果调用的是 reject,则拿着这个错误参数直接执行 defaultReturnPromisereject
  7. 如果 resolve 中执行的又是一个新的 promise ,则重复执行上述规则(2-6)

new Promise轮廓

class Promise {
  constructor(executor) {
    let resolve = success_value => {};
    let reject = fail_reason => {};
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {}
}

状态不可逆

Promise/A+ (2.1) promise状态: 必须处于以下三种状态中的一种 pending( 等待 )、fulfilled( 完成 )、rejected( 失败 )

pending 可以转换成 fulfilledrejected,只要转换了完成或失败,则不可逆

class Promise {
  constructor(executor) {
    this.status = 'pending';// 默认状态
    this.success_data;// 成功传递的数据
    this.fail_reason;// 失败传递的数据
    // 执行成功
    let resolve = success_data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';// 变更为成功状态
        this.success_data = success_data;// 把内容传递进success_data中
      }
    };
    // 执行失败
    let reject = fail_reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = fail_reason;
      }
    };
    // new Promise的第一个参数会立即执行
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulFilled(this.success_data);
    }
    if (this.status === 'rejected') {
      onRejected(this.fail_reason);
    }
  }
}

同一个实例可以被多次then

class Promise {
  constructor(executor) {
    this.status = 'pending';// 默认状态
    this.success_data;// 成功传递的数据
    this.fail_reason;// 失败传递的数据
    this.onResolvedAry = [];// 存放成功的回调
    this.onRejectedAry = [];// 存放失败的回调
    // 执行成功
    let resolve = success_data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.success_data = success_data;// 把内容传递进来
        this.onResolvedAry.forEach(item => item());// 遍历缓存列表,依次触发里面的回调函数
      }
    };
    // 执行失败
    let reject = fail_reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = fail_reason;
        this.onRejectedAry.forEach(item => item());
      }
    };
    // new Promise的第一个参数会立即执行
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulFilled(this.success_data);
    }
    if (this.status === 'rejected') {
      onRejected(this.fail_reason);
    }
    // 异步执行时,status状态处于pending状态
    if (this.status === 'pending') {
      // 同一个实例可以被多次then,在异步成功后调用resolve或者reject方法,以分别执行onResolvedAry,onRejectedAry两个数组
      // 添加到缓存列表、以供异步成功后执行的resolve或reject所循环调用
      this.onResolvedAry.push(onFulFilled(this.success_data));
      this.onRejectedAry.push(onRejected(this.fail_reason));
    }
  }
}

.then方法是异步的

// 需要在then的状态判断下添加setTimeout(() => {}, 0)来模拟异步效果

.then方法默认返回新的Promise(链式调用)

.then 方法中,状态判断下添加 - 返回新的 new Promise( 链式调用 ),在 promise 添加 setTimeout 以模拟异步执行

// 需要在then的状态判断下添加并返回一个新的Promise
class Promise {
  constructor(executor) {
    this.status = 'pending';// 默认状态
    this.success_data;// 成功传递的数据
    this.fail_reason;// 失败传递的数据
    this.onResolvedAry = [];// 存放成功的回调
    this.onRejectedAry = [];// 存放失败的回调
    // 执行成功
    let resolve = success_data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.success_data = success_data;// 把内容传递进来
        this.onResolvedAry.forEach(item => item());
      }
    };
    // 执行失败
    let reject = fail_reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = fail_reason;
        this.onRejectedAry.forEach(item => item());
      }
    };
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {
    let promise2;
    if (this.status === 'fulfilled') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          onFulFilled(this.success_data);
        }, 0);
      });
    }
    if (this.status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          onRejected(this.fail_reason);
        }, 0);
      });
    }
    // 异步执行时,status处于pending状态
    // new Promise的第一个参数会立即执行,
    if (this.status === 'pending') {
      // 同一个实例可以被多次then,在异步成功后调用resolve或者reject方法,以分别执行onResolvedAry,onRejectedAry两个数组
      promise2 = new Promise((resolve, reject) => {
        setTimeou(() => {
          this.onResolvedAry.push(onFulFilled(this.success_data));
          this.onRejectedAry.push(onRejected(this.fail_reason));
        }, 0);
      });
    }
  }
}

.then方法中用户可以显示的返回Promise,并可以多层嵌套

截取其中的一段代码

then(onFulFilled, onRejected) {
  let promise2;
  if (this.status === 'fulfilled') {
    promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let x = onFulFilled(this.success_data);
        resolvePromise(promise2, x, resovle, reject);
        // x为用户输入的返回值,p.then(data => { return "123" })
        // 如果是非promise的话,此时可以直接调用新的promise2的resolve方法
        // 如果是promise,那么就需要在then中拿到异步回来的参数,以调用promise2的resolve方法
        // 因为resolve或者reject中可以嵌套promise所以,所以我们需要把这个判断抽离出一个函数,以方便递归调用自己
      }, 0);
    });
  }
}

resolvePromise(promise2, x, resolve, reject) {
  // typeof null === object
  if(x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let then = x.then;
    // 判断then是否是函数,也许是一个对象
    if (typeof then === 'function') {
      then.call(x, (data) => {
        // 如果resolve中还是一个promise,需要递归处理,直到出现不是promise值为止,然后调用promise2的resolve或reject。以返回到主流程的下一个then中
        resolvePromise(promise2, data, resolve, reject);
      }, (error) => {
        reject(error);
      });
    } else {
      // 如果不是一个函数,可能是一个JSON
      resolve(x);
    }
  } else {
    // 如果不是promise就直接调用promise2的resolve
    resolve(x)
  }
}

.then的代码

// 解析默认返回的promise和用户输入的promise之间的关系
function resolvePromise(promise2, x, resolve, reject) {
  // 这里面的resolve和reject都是promise2的
  // 判断x是否是Promise(核心)
  if (promise2 === x) {
    // 2.3.1 如果promise和x指向同一个对象,爆出TypeError类型错误,表示拒绝
    return reject(new TypeError('引用错误'));
  }
  // 2.3.2 如果x是一个对象或者函数,再去取then方法
  // typeof null === object
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 如果去检索x.then方法,抛出了异常,那么久应该reject异常抛出
    let onOff;// 防止成功之后再调取失败。。。为了兼容别人的Proimse
    try {// 防止取then时,报错
      let then = x.then;// 2.3.3.1 取x的then方法
      // 2.3.3.3 如果then是一个函数,就认为其实Promise
      if (typeof then === 'function') {
        // 让x.then执行。用户输入的promise执行then方法
        then.call(x, (data) => {
          if (onOff) return;
          onOff = true;
          // data有可能还是一个Promise
          // 递归解析Promise
          resolvePromise(promise2, data, resolve, reject);
        }, (error) => {
          if (onOff) return;
          onOff = true;
          reject(error);
        });
      } else {
        resolve(x);
      }
    } catch (error) {
      if (onOff) return;
      onOff = true;
      reject(error);
    }
  } else {// 2.3.4 如果不是object或function 则直接fulfill 成功
    // 如果是一个普通值,则直接调用resolve把普通值传递进去
    resolve(x);
  }
}

class Promise {
  constructor(executor) {
    this.status = 'pending';
    this.success_value;
    this.fail_reason;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';// 变更状态
        this.success_value = data;// 传递值
        this.onResolvedCallbacks.forEach(item => item());
      }
    }
    let reject = reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = reason;
        this.onRejectedCallbacks.forEach(item => item());
      }
    }
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(onFulFilled, onRejected) {
    let promise2;
    // 添加链式操作
    if (this.status === 'fulfilled') {
      // 执行完成之后,返回一个新的Promise,以方便下一次链式调用
      promise2 = new Promise((resolve, reject) => {
        // 查看onFulFilled执行完的结果是什么?
        let x = onFulFilled(this.success_value);
        // 如果x返回的是一个普通值,则把这个普通只作为promise2成功的结果
        // 如果x是Promise,则取其结果,作为promise2成功的结果
        // 现在需要判断x和promise2的关系
        // promise2是then中执行完毕后的回调Proimse
        // x是 then中用户输入写的return值
        // 如果用户return的是一个普通值:那么需要传递給Promise2()的成功结果
        // 如果用户return的是一个Promise:那么需要判定是成功还是失败

        // 解析promise,去x的结果,然后让promise2成功或者失败
        resolvePromise(promise2, x, resolve, reject);
      });
    }
    if (this.status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        let x = onRejected(this.fail_reason);
        resolvePromise(promise2, x, resolve, reject);
      });
    }
    if (this.status === 'pending') {
      promise2 = new Promise((resolve, reject) => {
        // 存储值,在异步成功之后循环当前数组执行
        this.onResolvedCallbacks.push(() => {
          let x = onFulFilled(this.success_value);
          resolvePromise(promise2, x, resolve, reject);
        });
        this.onRejectedCallbacks.push(() => {
          let x = onRejected(this.fail_reason);
          resolvePromise(promise2, x, resolve, reject);
        });
      });
    }
    return promise2;
  }
}

.catch方法

// catch就是then的第二个错误的简写
catch(onRejected) {
  return this.then(null, onRejected);
}

Promise.resolve

Promise 上的静态方法 resolvereject ,直接调用静态方法中返回的新的 Promise 中的resolvereject即可

Promise.resolve = function (value) {
  return new Promise((resolve, reject) => resolve(value));
}
Promise.reject = function (value) {
  return new Promise((resolve, reject) => reject(value));
}

Promise.all([p1, p2, p3])

  1. 数组中所有的异步数据回来后,才执行 then 中成功回调
  2. 循环数组的 then 方法,如果 then 的数值 all 回来了,则调用新的 Promiseresolve,如果没有全部回来,则调 reject
  3. 如果数组中的异步直接调取了 reject 的,则直接调用新的 Promisereject
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let idx = 0;// 为了保证获取全部成功,来设置的索引.不能使用arr.length来判断,如果数组最后的异步先回来,那么此时arr和promises的长度就相等了 - 乱套了
    function processData(index, data) {
      // 和all的数组一一对应,arr数组和promises长度相等时成功
      arr[index] = data;
      // 給第二个复制,第一个的数值就是空的undefined,万一数组中的最后一个回来他们就都成功了 arr.length === promises.lenght
      idx++;
      if (idx === promises.length) {
        resolve(arr);
      }
    }
    for (let i = 0; i < promises.length; i++) {
      promises[i].then((data) => {
        // all 要根据数组的顺序来存放
        processData(i, data);
      }, reject);
    }
  });
}

Promise.race

第一个参数中只要有一个异步数据回来,就执行

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      // 只要有一个异步数据回来,誰回来的早就搞誰
      promises[i].then(resolve, reject);
    })
  });
}

附:这篇博客 也许 想表达 promise 其实相对而言比较简单 (⊙﹏⊙)b