如何在 Edge 中添加 polyfill 以支持 finally()?

Posted

技术标签:

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

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

【问题讨论】:

你可以考虑看看这个库:github.com/es-shims/Promise.prototype.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() - startms`);
;

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-wrappermax-height:100%!important

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

【讨论】:

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

以上是关于如何在 Edge 中添加 polyfill 以支持 finally()?的主要内容,如果未能解决你的问题,请参考以下文章

获取 polyfill 在 Edge(或 IE)上不起作用

Shim 与 Polyfill

如何安装 babel-polyfill 库?

获取参数在 Edge 14 上不起作用的 API

如何将 polyfill 添加到 nuxt 2.0?

向 ES6 添加 Promise polyfill