【问题标题】:How to add a polyfill to support finally() in Edge?如何在 Edge 中添加 polyfill 以支持 finally()?
【发布时间】:2019-04-19 01:02:14
【问题描述】:

我正在使用 axios 库并使用 then()、catch() 和 finally()。在 Chrome 中完美运行。但是 finally() 方法在 MS Edge 中不起作用。我使用 polyfills 或 shims 进行了研究,但我迷路了。我没有使用 webpack 或转译,也不打算添加它们。我需要保持简单。如何添加 polyfill 以确保 finally() 在 Edge 中工作?谢谢!

【问题讨论】:

标签: javascript promise polyfills finally


【解决方案1】:

除了下面详述的行为之外,这应该处理 thenable 的 species 的传播:

Promise.prototype.finally = Promise.prototype.finally || {
  finally (fn) {
    const onFinally = callback => Promise.resolve(fn()).then(callback);
    return this.then(
      result => onFinally(() => result),
      reason => onFinally(() => Promise.reject(reason))
    );
  }
}.finally;

此实现基于 finally() 的记录行为,并取决于 then() 是否符合规范:

  • finally 回调不会收到任何参数,因为没有可靠的方法来确定承诺是否被履行或拒绝。此用例正好适用于您不关心拒绝原因或履行价值的情况,因此无需提供。

  • Promise.resolve(2).then(() => {}, () => {})(将使用undefined 解析)不同,Promise.resolve(2).finally(() => {}) 将使用2 解析。

  • 同样,与Promise.reject(3).then(() => {}, () => {})(将使用undefined 实现)不同,Promise.reject(3).finally(() => {}) 将被3 拒绝。

注意:finally 回调中的throw(或返回被拒绝的承诺)将拒绝新的承诺,并在调用throw() 时指定拒绝原因。

当然还有等效行为的演示:

const logger = (label, start = Date.now()) => (...values) => {
  console.log(label, ...values, `after ${Date.now() - start}ms`);
};

const delay = (value, ms) => new Promise(resolve => {
  setTimeout(resolve, ms, value);
});

// run test on native implementation
test('native');

// force Promise to use the polyfill implementation
Promise.prototype.finally = /* Promise.prototype.finally || */ {
  finally (fn) {
    const onFinally = callback => Promise.resolve(fn()).then(callback);
    return this.then(
      result => onFinally(() => result),
      reason => onFinally(() => Promise.reject(reason))
    );
  }
}.finally;

// run test on polyfill implementation
test('polyfill');

function test (impl) {
  const log = ordinal => state => logger(`${ordinal} ${impl} ${state}`);
  const first = log('first');

  // test propagation of resolved value
  delay(2, 1000)
    .finally(first('settled'))
    .then(first('fulfilled'), first('rejected'));

  const second = log('second');

  // test propagation of rejected value
  delay(Promise.reject(3), 2000)
    .finally(second('settled'))
    .then(second('fulfilled'), second('rejected'));

  const third = log('third');

  // test adoption of resolved promise
  delay(4, 3000)
    .finally(third('settled'))
    .finally(() => delay(6, 500))
    .then(third('fulfilled'), third('rejected'));

  const fourth = log('fourth');

  // test adoption of rejected promise
  delay(5, 4000)
    .finally(fourth('settled'))
    .finally(() => delay(Promise.reject(7), 500))
    .then(fourth('fulfilled'), fourth('rejected'));
}
.as-console-wrapper{max-height:100%!important}

感谢 @Bergi 对此答案的意见。如果您觉得这篇文章有帮助,请查看his implementation 并点赞。

【讨论】:

  • 内置 finally 函数是否保证在所有 then()catch() 之后被调用。如果没有,这可能会有风险
  • @Evert 不,它没有,.finally() 是按照它在链中的顺序调用的,就像任何其他 promise 方法一样。
  • 谢谢帕特里克,我刚刚测试了你的代码,完美运行!我认为我让 polyfill 过于复杂,现在我意识到它们相当简单。
  • @PatrickRoberts 我看不出有什么问题。它与更新后的基本相同,只是状态传播方式不同。我较短的方法——返回原始承诺作为结果——有点奇怪,但我相信它有效。队列中的承诺作业数量可能与规范要求的不同,但我忽略了这一点。
  • 顺便说一句,关于物种,我认为通过返回 then 方法创建的内容,我们做的一切都是正确的。唯一的区别是 Promise.resolve 而不是 this.constructor.resolve 但 imo 这无关紧要
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
  • 2014-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多