对齐es6 Promise api

100 阅读4分钟

这里主要是实现es6除了then的其他api,如果需要主体(then)的实现,可以点这里传送门,这些其余的api都是围绕着then去展开的,实现起来都比较简单,这里是非标准实现,没有去扒拉定义,只是对照了下效果。还有一点是这个是使用我自己实现的 Thenable 去实现的。我简单演示下效果

const mp = CreateThenable() // 创建 thenable
mp.resolve //调用触发 resolve
mp.reject //调用触发 reject

转换为es6 Promise 用法是这样的,具体实现还是建议点传送门过去看看 thenable 的实现,下面的实现我不会用这个去实现,还是为了方便会用 CreateThenable 去创建转接 thenable ,自行去替换成下面的P或者Promise。

function P(fn) {
  const mp = CreateThenable();
  try {
    fn(mp.resolve, mp.reject);
  } catch (e) {
    mp.reject(e);
  }

  return mp;
}

原型链函数 catch

这个最简单的,就是不传 onFulfilled 回调就成,实现就是这么简单;居于 Promise 改是就是加上this就好,举例这样,其他api就不列出来了,有什么问题评论区提出来,不保绝对正确

function (onRejected) {
  return then(null, onRejected);
}

// Promise
Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
}

原型链函数 finally

finally是在Promise完成时调用不管什么状态(fulfilled | rejected),我也没怎么用过,我还专门去查了下别人的实现,发现网上的实现和MDN用法以及类型和个人实测有出入(没有找到简短的实现是正确的,看文章要去辨别)。其实看下IDE类型提示就可以知道了

finally.pngthen catch 一样传入一个函数进去,也是返回一个 Promise,但是很不一样的地方就是这个函数是个无入参且无返回值的函数。无入参也就是无法获取完成后的结果和状态不知道是fulfilled还是 rejected,无返回值就是没办法影响后续返回的Promise状态,看图返回是Promise<number>这个是继承于第一个resolve得来的状态,经过实测传入的函数不管返回什么都不会影响下一个Promise的结果和运行,后来又想了下,如果我们传入的函数运行错了呢会怎样?

Promise.resolve('resolve').finally(() => {
  throw 'finally'
}).then((d) => {
  console.log(d); // 未调用
}).catch((e) => {
  console.log(e); // finally
})

注意了,会改变返回的Promise,这就很糟糕,使用起来要注意了,不要随意在finally后面继续接调用链了

结论就是 finallyPromise完成时调用,并且中转上一个Promise(自己当前所在的这个)的状态给自己返回的生成的新的Promise,但是调用失败会改变新的Promise

function (onFinally) {
  function resolve(result) {
    onFinally(); // 回调错了不用我们管 Thenable自己会捕获
    return result;
  }
  function reject(e) {
    onFinally(); // 回调错了不用我们管 Thenable自己会捕获
    throw e; // 要抛出错误才能被捕获成 reject
  }

  return then(resolve, reject);
}

静态函数 resolve/reject

这个比较简单,直接返回resolve/rejectPromise就好。

P.resolve = function (PromiseResolveParam) {
  // 自行替换 new Promise 写法,不会请评论区留言,或者私我
  const thenable = CreateThenable(); 

  thenable.resolve(PromiseResolveParam);

  return thenable;
};

静态函数 race

这个也比较简单,创建一个主的 Thenable 返回,然后对接迭代对象中的每一个 Thenable,然后有可能数组里的可能不是 Thenable,我们得创建一层包裹下,让他明确为一个 Thenable,对接好后谁先触发自然就调谁了。

P.race = function (PromiseRaceParam) {
  const ret = CreateThenable();

  try { // 捕获错误,不是可迭代对象它会报错,不单独去做判断
    for (const thenable of PromiseRaceParam) {
      const handle = CreateThenable();
      handle.resolve(thenable);
      handle.then(ret.resolve, ret.reject);
    }
  } catch (e) {
    ret.reject(e);
  }

  return ret;
};

静态函数 all

allrace,区别是在fulfilled情况,需要等待全部 fulfilledrejected处理和race一样

P.all = function (PromiseAllParam) {
  const ret = CreateThenable();
  const data = []; // fulfilled 结果数组
  let length = 0; // 自己记录传入迭代器的length的长度,不要去读传入迭代器的length可能会出错
  let done = 0;

  function resolve(index) {
    return function (d) {
      data[index] = d;
      done += 1;
      if (done === length) {
        ret.resolve(data);
      }
    };
  }

  try {
    for (const thenable of PromiseAllParam) {
      const index = length; // 返回值有顺序,记录对应顺序
      length += 1;

      const handle = CreateThenable();
      handle.resolve(thenable);
      handle.then(resolve(index), ret.reject);
    }
    if (length === 0) {
      ret.resolve(data);
    }
  } catch (e) {
    ret.reject(e);
  }

  return ret;
};

静态函数 allSettled

all基本一样,它会在fulfilled返回{status,result}数组,就是全部结果都要处理,入参正确没有rejected的情况

P.allSettled = function (PromiseAllParam) {
  const ret = CreateThenable();
  const data = [];
  let length = 0;
  let done = 0;

  function handler(index, status) {
    return function (d) {
      // 返回编辑相应的状态
      if (status === STATES.REJECTED) {
        data[index] = {
          status: "rejected",
          reason: d,
        };
      } else {
        data[index] = {
          status: "fulfilled",
          value: d,
        };
      }

      done += 1;
      if (done === length) {
        ret.resolve(data);
      }
    };
  }

  try {
    for (const thenable of PromiseAllParam) {
      const index = length;
      length += 1;

      const handle = CreateThenable();
      handle.resolve(thenable);
      handle.then(
        handler(index, STATES.FULFILLED),
        handler(index, STATES.REJECTED)
      );
    }
    if (length === 0) {
      ret.resolve(data);
    }
  } catch (e) {
    ret.reject(e);
  }

  return ret;
};

有什么问题可以私聊或评论留言,源码地址