【问题标题】:Catching promise errors inside another promise's callback在另一个 Promise 的回调中捕获 Promise 错误
【发布时间】:2019-04-07 10:58:11
【问题描述】:

下面的代码按预期运行。如果调用charge函数,该函数会从firestore获取相关票证对象,然后返回给客户端。

如果票证不存在,该函数将抛出 HttpsError 并带有错误消息,将由客户端解析。

exports.charge = functions.https.onCall(data => {
  return admin.firestore().collection('tickets').doc(data.ticketId.toString()).get()
    .then((snapshot) => {
      return { ticket: snapshot.data() }
    })
    .catch((err) => {
      throw new functions.https.HttpsError(
        'not-found', // code
        'The ticket wasn\'t found in the database'
      );
    });
  });

问题出现在这之后。我现在需要使用 Stripe 向用户收费,这是另一个异步进程,将返回 Promise。收费需要第一个异步方法获取的定价信息,所以需要在检索到snapshot后调用。

exports.charge = functions.https.onCall(data => {
  return admin.firestore().collection('tickets').doc(data.ticketId.toString()).get()
    .then((snapshot) => {
      return stripe.charges.create(charge) // have removed this variable as irrelevant for question
        .then(() => {
          return { success: true };
        })
        .catch(() => {
          throw new functions.https.HttpsError(
            'aborted', // code
            'The charge failed'
          );
        })
    })
    .catch(() => {
      throw new functions.https.HttpsError(
        'not-found', // code
        'The ticket wasn\'t found in the database'
      );
    });
  });

我的问题是在新的charge 请求中捕获错误。似乎如果收费失败,它会成功调用第一个'aborted'catch,但随后将其传递给父catch,错误被覆盖,应用程序看到'ticket not found'错误。

我怎样才能阻止这种情况发生?我需要分别捕获这两个错误并为每个错误抛出一个HttpsError

【问题讨论】:

    标签: javascript node.js firebase promise google-cloud-functions


    【解决方案1】:

    一般来说,此类问题可以通过添加status 节点然后与最终的then 块链接来解决。您可以尝试以下操作

    exports.charge = functions.https.onCall(data => {
      return admin.firestore().collection('tickets').doc(data.ticketId.toString()).get()
        .then((snapshot) => {
          return stripe.charges.create(charge)
            .then(() => {
              return { success: true };
            })
            .catch(() => {
                 return {
                    status : 'error', 
                    error : new functions.https.HttpsError(
                'aborted', // code
                'The charge failed',
                { message: 'There was a problem trying to charge your card. You have NOT been charged.' }
              )};
            })
        })
        .catch(() => {
          return {
             status : 'error',
             error : new functions.https.HttpsError(
            'not-found', // code
            'The ticket wasn\'t found in the database',
            { message: 'There was a problem finding that ticket in our database. Please contact support if this problem persists. You have NOT been charged.' }
          )};
        }).then((response) => {
             if(response.status === 'error') throw response.error;
             else return response;
        });
      });
    

    【讨论】:

      【解决方案2】:

      不要将then 嵌套在另一个then 中以完成多项工作:

      work1
      .then((work1_results) => {
          return work2.then((work2_results) => {
              // this is bad
          })
      })
      

      相反,将所有工作作为一个链式序列执行:

      work1
      .then((work1_results) => {
          return work2
      })
      .then((work2_results) => {
          // handle the results of work2 here
      })
      

      如果您需要在回调之间累积数据,您可以将中间结果存储在更高范围的变量中。

      【讨论】:

      • 谢谢,但我可以预见这仍然有同样的问题。如果我链接then 并在每个之后放置一个catch,那么最后一个catch 是否不会在每次覆盖之前抛出的任何错误时都被调用?
      • 为什么每个阶段都需要投掷?只需定义下一个阶段应该做的你自己的返回对象吗?
      • 无论如何,嵌套的 then 是一种反模式。如果您使用的是 eslint,它会抱怨这一点..
      猜你喜欢
      • 2019-09-25
      • 2015-07-21
      • 2021-04-21
      • 1970-01-01
      • 2019-09-17
      • 1970-01-01
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多