0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

手写Promise

修罗 2020-12-22 1906 0条评论 JS

首页 / 正文

手写Promise

1、定义整体结构

(function (window) {
  // excutor: 内部同步执行的函数 (resolve, reject) => {}
  function Promise(excutor) {}

  // 为promise指定成功/失败的回调,返新的promise
  Promise.prototype.then = function (onResolved, onRejected) {};

  // 为promise指定失败的回调,是then(null, onRejected)的语法糖
  Promise.prototype.catch = function (onRejected) {};

  // 返回一个指定了成功value的promis
  Promise.resolve = function (value) {};

  // 返回一个指定了失败reason的promise
  Promise.reject = function (reason) {};

  // 返回一个promise, 只有promises中所有promise都成功时, 才最终成功, 只要有一个失败就直接失败
  Promise.all = function (promises) {};

  // 返回一个promise, 一旦某个promise解决或拒绝, 返回的promise就会解决或拒绝。
  Promise.race = function (promises) {};

  // 暴露构造函数
  window.Promise = Promise;
})(window);

2、Promise 构造函数的实现

// excutor: 内部同步执行的函数 (resolve, reject) => {}
function Promise(excutor) {
  const self = this;
  // 状态值, 初始状态为 pending, 成功了变为resolved, 失败了变为 rejected
  self.status = "pending";
  // 用来保存成功 value 或失败 reason 的属性
  self.data = undefined;
  // 用来保存所有待调用的包含 onResolved 和 onRejected 回调函数的对象的数组
  self.callbacks = [];

  /*异步处理成功后应该调用的函数
    value: 将交给 onResolve()的成功数据
  */
  function resolve(value) {
    if (self.status !== "pending") {
      // 如果当前不是 pending, 直接结束
      return;
    }
    // 立即更新状态, 保存数据
    self.status = "resolved";
    self.data = value;
    // 异步调用所有待处理的 onResolved 成功回调函数
    if (self.callbacks.length > 0) {
      setTimeout(() => {
        self.callbacks.forEach((obj) => {
          obj.onResolved(value);
        });
      });
    }
  }

  /*异步处理失败后应该调用的函数
    reason: 将交给 onRejected()的失败数据
  */
  function reject(reason) {
    if (self.status !== "pending") {
      // 如果当前不是 pending, 直接结束
      return;
    }
    // 立即更新状态, 保存数据
    self.status = "rejected";
    self.data = reason;
    // 异步调用所有待处理的onResolved成功回调,同步代码length为0,resolve被包裹在定时器中等异步情况才不为0
    setTimeout(() => {
      self.callbacks.forEach((obj) => {
        obj.onRejected(reason);
      });
    });
  }
  try {
    // 立即同步调用 excutor()处理
    excutor(resolve, reject);
  } catch (error) {
    // 如果出了异常, 直接失败
    reject(error);
  }
}

3、promise.then()/catch()的实现

// 为promise指定成功/失败的回调,返新的promise
Promise.prototype.then = function (onResolved, onRejected) {
  const self = this;
  // 如果 onResolved/onRejected 不是函数, 可它指定一个默认的函数
  onResolved =
    // 指定返回的 promise 为一个成功状态, 结果值为 value
    typeof onResolved === "function" ? onResolved : (value) => value;
  onRejected =
    // 指定返回的 promise 为一个失败状态, 结果值为 reason
    typeof onRejected === "function"
      ? onRejected
      : (reason) => {
          throw reason;
        };

  // 返回一个新的 promise 对象
  return new Promise((resolve, reject) => {
    /*专门抽取的用来处理 promise 成功/失败结果的函数
      callback: 成功/失败的回调函数
    */
    function handle(callback) {
      // 1. 抛出异常 ===> 返回的 promise 变为 rejected
      try {
        const x = callback(self.data);
        // 2. 返回一个新的 promise ===> 得到新的 promise 的结果值作为返回的promise 的结果值
        if (x instanceof Promise) {
          // 一旦 x 成功了, resolve(value), 一旦 x失败了: reject(reason);
          x.then(resolve, reject);
        } else {
          // 3. 返回一个一般值(undefined) ===> 将这个值作为返回的 promise 的成功值;
          resolve(x);
        }
      } catch (error) {
        reject(error);
      }
    }

    if (self.status === "resolved") {
      // 当前 promise 已经成功了
      setTimeout(() => {
        handle(onResolved);
      });
    } else if (self.status === "rejected") {
      // 当前 promise 已经失败了
      setTimeout(() => {
        handle(onRejected);
      });
    } else {
      // 当前 promise 还未确定 pending (例:resolve被包裹在定时器中,执行到then,status为pending)
      // 将 onResolved 和 onRejected 保存起来
      self.callbacks.push({
        onResolved(value) {
          handle(onResolved);
        },
        onRejected(reason) {
          handle(onRejected);
        },
      });
    }
  });
};

// 为promise指定失败的回调,是then(null, onRejected)的语法糖
Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
};

4、Promise.resolve()/reject()的实现

// 返回一个指定了成功value的promise
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(resolve, reject);
    } else {
      resolve(value);
    }
  });
};

// 返回一个指定了失败reason的promise
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
};

5、Promise.all/race()的实现

// 返回一个promise, 只有promises中所有promise都成功时, 才最终成功, 只要有一个失败就直接失败
Promise.all = function (promises) {
  // 返回一个新的 promise
  return new Promise((resolve, reject) => {
    // 已成功的数量
    let resolvedCount = 0;
    // 待处理的 promises 数组的长度
    const promisesLength = promises.length;
    // 准备一个保存成功值的数组
    const values = new Array(promisesLength);
    // 遍历每个待处理的 promise
    for (let i = 0; i < promisesLength; i++) {
      // promises 中元素可能不是一个数组, 需要用 resolve 包装一下
      Promise.resolve(promises[i]).then(
        (value) => {
          // 成功当前 promise 成功的值到对应的下标
          values[i] = value;
          // 成功的数量加 1
          resolvedCount++;
          // 一旦全部成功
          if (resolvedCount === promisesLength) {
            // 将所有成功值的数组作为返回 promise 对象的成功结果值
            resolve(values);
          }
        },
        (reason) => {
          // 一旦有一个promise产生了失败结果值, 将其作为返回promise对象的失败结果值;
          reject(reason);
        }
      );
    }
  });
};

// 返回一个promise, 一旦某个promise解决或拒绝, 返回的promise就会解决或拒绝。
Promise.race = function (promises) {
  // 返回新的 promise 对象
  return new Promise((resolve, reject) => {
    // 遍历所有 promise
    for (var i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then(
        (value) => {
          // 只要有一个成功了, 返回的 promise 就成功了
          resolve(value);
        },
        (reason) => {
          // 只要有一个失败了, 返回的结果就失败了
          reject(reason);
        }
      );
    }
  });
};

扩展:Promise.resolveDelay()/rejectDelay()的实现

// 返回一个延迟指定时间才确定结果的 promise 对象
Promise.resolveDelay = function (value, time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (value instanceof Promise) {
        // 如果 value 是一个 promise, 取这个promise 的结果值作为返回的 promise 的结果值
        value.then(resolve, reject); // 如果 value 成功, 调用resolve(val), 如果 value 失败了, 调用 reject(reason)
      } else {
        resolve(value);
      }
    }, time);
  });
};

// 返回一个延迟指定时间才失败的 Promise 对象。
Promise.rejectDelay = function (reason, time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(reason);
    }, time);
  });
};

测试

<script>
  // 如果是一般值, p1成功, value就是这个值
  const p1 = Promise.resolve(2); 
  // 如果是成功的promise, p2成功, value是这个promise的value
  const p2 = Promise.resolve(Promise.resolve(3));
  // 如果是失败的promise, p3失败, reason是这个promise的reason
  const p3 = Promise.resolve(Promise.reject(4)); 
  p1.then((value) => {
    console.log("p1", value);
  });
  p2.then((value) => {
    console.log("p2", value);
  });
  p3.catch((reason) => {
    console.log("p3", reason);
  });

  // const p4 = new Promise((resolve) => {
  //   setTimeout(() => {
  //     resolve(5)
  //   }, 1000);
  // })
  const p4 = Promise.resolveDelay(5, 1000);
  const p5 = Promise.reject(6);

  const pAll = Promise.all([p4, 7, p1, p2]);
  pAll.then(
    (values) => {
      console.log("race onResolved()", values);
    },
    (reason) => {
      console.log("race onRejected()", reason);
    }
  );

  // const pRace = Promise.race([p4, 7, p5, p2, p3])
  // pRace.then(
  //   value => {
  //       console.log('race onResolved()', value)
  //   },
  //   reason => {
  //       console.log('race onRejected()', reason)
  //   }
  // )

  const p6 = Promise.resolveDelay(66, 2000);
  const p7 = Promise.rejectDelay(77, 3000);
  p6.then((value) => {
    console.log("p6", value);
  });
  p7.catch((reason) => {
    console.log("p7", reason);
  });
</script>

1608593705229.png

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis