【问题标题】:Can you return a Promise with a predefined finally?你能用一个预定义的 finally 返回一个 Promise 吗?
【发布时间】:2021-08-07 15:15:21
【问题描述】:

我有一个返回 Promise 的函数。当我在 .then().catch() 块中使用完该 Promise 后,我总是想执行相同的清理代码。我目前的设置是这样的:

const promiseWithFinally = () => {
  return new Promise((resolve, reject) => {
    // resolve or reject at some point
  }).finally(() => console.log('finally done'))
}

promiseWithFinally()
  .then(() => console.log('then done'))
  .catch(() => console.log('catch done'))

我想要发生的是 then donecatch done 先被记录,然后是 finally done。然而,它似乎以完全相反的顺序执行 - 当我在 5 秒超时后解决承诺时,finally done 在 5 秒后首先被记录,然后then done 立即被记录。

我做错了什么或者一般情况下可以这样做吗?我知道我可以将.finally() 附加到每个单独的函数调用中,但由于它总是相同的,我想将它放在函数定义中。

【问题讨论】:

  • 一般来说,您不应该依赖以任何特定顺序调用相同承诺的处理程序。即使您将finallythen 附加到同一个promise,我认为它们也会按照您定义它们的顺序被调用。
  • @NiettheDarkAbsol 这就是他的代码不起作用的原因。
  • 你为什么期待promise最终变成这样?你会期望同步最终做到这一点吗?
  • 您可能正在寻找promise disposer pattern

标签: javascript promise


【解决方案1】:

不,这是不可能的。 finally 是为了在给定的 promise 之后进行清理,而不是为了它的 thencatch 方法。

您可以做的是将thencatch 方法传递给将附加在finally 之前的函数:

const promiseWithFinally = (chain) => {
  return new Promise((resolve, reject) => {
    // resolve or reject at some point
    setTimeout(resolve, 1000);
  }).then(chain.then, chain.catch).finally(() => console.log('finally done'))
}

promiseWithFinally({
  then: () => console.log('then done'),
  catch: () => console.log('catch done')
})

【讨论】:

  • 基本上和我的回答一样。
  • 我认为这个答案和前一个一样有缺陷,因为它让我想知道你是否想要一个承诺。这取决于 OP 未提供的一些细节。
  • 我认为一个很好的共识是你不能真正依赖 finally。
  • 可能是xy问题。我看不出有任何实际的理由来像这样链接承诺。
【解决方案2】:

简答

不,这是不可能的,因为您不能依赖 finally 何时运行。

更长的答案和可能的解决方案

代码

const cleanupFunc = () => {
  console.log('Cleaning up.');
  };

const someAsyncMethod = (thenFunc, catchFunc) => {
  new Promise(
    (resolve, reject) => {
      setTimeout(() => resolve(), 5000);
    },
  )
    .then((...args) => {
      try {
        thenFunc(...args);
      } catch (err) {
      }
      cleanupFunc();
    })
    .catch((...args) => {
      try {
        catchFunc(...args);
      } catch (err) {
      }
      cleanupFunc();
    });
};

someAsyncMethod((result) => console.log('Then done'), (err) => console.log('Catch done'));

说明

虽然不可能依赖 finally,但你可以做的是编写一个需要执行一些异步操作并返回一个承诺的函数。在我的示例中,此操作等待 5 秒超时,但他也可以是例如返回承诺的异步 api 调用。

下一步是向异步操作返回的 promise 添加 then 和 catch 调用,它们都以 try 子句开头,在该子句中您调用属于 resolve 类型的回调参数(thenFunc 为 then,catchFunc for catch) 后跟一个不做任何事情并通过调用 cleanup 函数结束的 catch。通过这种方式,您可以确定无论在运行 then 或 catch 回调期间发生什么,都会调用清理函数。

【讨论】:

    【解决方案3】:

    假设您知道该承诺的其余处理程序将被同步附加,并且处理程序中的所有操作都是同步的,这是可能的,尽管有点 hacky。

    只需让 finally 处理程序在最后重新附加自己:

    const promiseWithFinally = () => {
      const thisPromise = new Promise((resolve, reject) => {
        // Rejection example
        setTimeout(reject, 200);
      }).finally(() => {
        setTimeout(() => {
          thisPromise.finally(() => console.log('finally done')).catch(() => {});
        }, 0);
      });
      return thisPromise;
    };
    
    promiseWithFinally()
      .then(() => console.log('then done'))
      .catch(() => console.log('catch done'));
      

    【讨论】:

      猜你喜欢
      • 2011-10-26
      • 1970-01-01
      • 2017-07-22
      • 1970-01-01
      • 2022-01-21
      • 2014-10-11
      • 2010-12-02
      • 2016-07-02
      • 1970-01-01
      相关资源
      最近更新 更多