【问题标题】:How can I force this promise to throw specific errors?我怎样才能强制这个承诺抛出特定的错误?
【发布时间】:2018-05-23 21:31:09
【问题描述】:

代码是更大更复杂代码的一部分,所以我只是将相关的 sn-ps 放到我的问题中。

我有这个promise.all sn-p:

 Promise.all(receivedObjs.arrayCMsIds.map(cmid => 
                    server.writeAttachedCMinfo(invId,cmid)))
                            .then(function (results) { // for promise all
                                return res.json(apiHelp.success(results,"success"));

                            }).catch(function (error) {
                                res.json(apiHelp.error(error, error));
                            });

还有这个冗长复杂的 writeAttachedCMinfo 函数:

    server.writeAttachedCMinfo = function (invId,cmid) {
    return new Promise(function (resolve, reject) {


        console.log("writeAttachedCMinfo");
        console.log("invoiceId " + invId);
        console.log("cmid "+ cmid);

        var invoiceId = JSON.stringify(invId);
        var cmId = JSON.stringify(cmid);
        var invIdString = invoiceId;
        var cmIdString = cmId;

        invIdString = invIdString.slice(1, -1);
        cmIdString = cmIdString.slice(1, -1);

        var projection = 'gwCode certifiedInvoiceAmount buyerReference supplierReference invoiceNo invoiceSerialNo invoiceFiles creditMemos';
        ubiqInvoice.findById(invIdString, projection).then(function (dbInvoice) {

            var intInvCertifiedAmount = parseInt(dbInvoice.certifiedInvoiceAmount);


            creditMemo.findById(cmIdString).then(function (dbCreditMemo) {
                var intCreditMemoAmount = parseInt(dbCreditMemo.creditMemoAmount);

                if (intInvCertifiedAmount <= intCreditMemoAmount) {

                    console.log('cm bigger than invoice')

                    return new Error ('CMisbiggerThanInvoice');
                }

                if (dbCreditMemo.isAssociated) {

                    return new Error ('CMisAssociated');
                }

                if (dbInvoice.gwCode === "100000000000"
                    || dbInvoice.gwCode === "110000000000"
                    || dbInvoice.gwCode === "111200000000"
                    || dbInvoice.gwCode === "111100000000"
                    || dbInvoice.gwCode === "111110000000"
                ) { 

                    var creditMemoEntry = {

                        id: guid.create().value,
                        batchId: dbCreditMemo.batchId,
                        invoiceId: dbInvoice._id,
                        recordTypeCode: "CM",
                        buyerReference: dbInvoice.buyerReference,
                        supplierReference: dbInvoice.supplierReference,
                        creditMemoNo: dbCreditMemo.creditMemoNo,
                        creditMemoIssuingDate: dbCreditMemo.creditMemoIssuingDate,
                        creditMemoEffectiveDate: dbCreditMemo.creditMemoEffectiveDate,
                        lastModificationDate: dbCreditMemo.lastModificationDate,
                        currencyCode: dbCreditMemo.currencyCode,
                        creditMemoAmount: dbCreditMemo.creditMemoAmount,
                        hashCode: dbCreditMemo.hashCode,
                        description: dbCreditMemo.description,
                        uploadDate: dbCreditMemo.uploadDate,
                        isAssociated: true,
                    }


                    dbInvoice.creditMemos.push(creditMemoEntry);
                    dbInvoice.certifiedInvoiceAmount = dbInvoice.certifiedInvoiceAmount - dbCreditMemo.creditMemoAmount;
                    dbInvoice.save();


                    dbCreditMemo.isAssociated = true;

                    dbCreditMemo.save();

                    resolve(dbInvoice)


                }
                else { return new Error ('wrongggwcode'); }

    })

        });

    }), function (error) {
        console.log("error: " + error);
    }

}

我的目标是在不满足 if 条件之一的情况下强制抛出错误,并且我想以自定义消息的形式将错误传递给客户端,以便我可以在客户端上使用它来表示显示各种错误,例如'CMisbiggerThanInvoice'

if (intInvCertifiedAmount <= intCreditMemoAmount) {

                        console.log('cm bigger than invoice')

                        return new Error ('CMisbiggerThanInvoice');
                    }

我只是想找出一种方法将错误从 writeAttachedCMinfo 函数传递给 promise.all 的 .catch(function (error) 但它不起作用,即使其中一个 promise.all 总是返回成功如果条件不满足。

我试过reject('CMisbiggerThanInvoice')、reject(new Error('CMisbiggerThanInvoice')...都一样。

如何才能真正强制 promise 函数返回错误?

【问题讨论】:

  • 调用reject 应该有效。当没有错误时,您是否获得了预期的results?另外请修复代码缩进,以便我们可以轻松解析嵌套。
  • 嗨,Bergi,是的,当没有错误时,我得到了预期的结果 - 但当出现错误时,没有任何结果,例如拒绝(新错误('fail'))不起作用,或者是还有其他方法可以拒绝吗?
  • 这两个存档怎么样?它们是同步的还是异步的?
  • 另外,您是否意识到,因为只有一个invId,所以只有一个dbInvoice,并且不需要在每次调用server.writeAttachedCMinfo() 时从数据库中一遍又一遍地检索它。事实上,它可能不应该被一遍又一遍地检索,否则每个dbInvoice.save() 都有覆盖同一整体事务中早期保存的危险。累积所有 creditMemo 并逐渐减少 certifiedInvoiceAmount 然后执行单个 `dbInvoice.save() 似乎更安全。

标签: javascript node.js promise


【解决方案1】:

server.writeAttachedCMinfo() 的主要内容是:

另外,因为只有一个invId,所以实际上只有一个dbInvoice,并且不需要在每次调用server.writeAttachedCMinfo() 时从数据库中一遍又一遍地检索它。事实上,它不应该被一遍又一遍地检索,否则每个dbInvoice.save() 很可能会覆盖同一整体事务中的早期保存。在单个 dbInvoice 对象中累积所有 creditMemos 并逐步减少 certifiedInvoiceAmount 并最终执行单个 `dbInvoice.save() 会更安全。

server.writeAttachedCMinfo = function(dbInvoice, cmid) {
    return creditMemo.findById(JSON.stringify(cmid).slice(1, -1))
    .then(dbCreditMemo => {
        if(parseInt(dbInvoice.certifiedInvoiceAmount) <= parseInt(dbCreditMemo.creditMemoAmount)) {
            throw new Error('CMisbiggerThanInvoice');
         // ^^^^^
        }
        /* all sorts of synchronous stuff */
        /* all sorts of synchronous stuff */
        /* all sorts of synchronous stuff */
        return dbCreditMemo; // deliver dbCreditMemo via returned Promise
    });
}

现在,在调用者中:

  • ubiqInvoice.findById() 可以调用一次。
  • 对dbInvoice 执行检查,并抛出失败。
  • 在每次通话时将dbInvoice 传递给invId,而不是server.writeAttachedCMinfo()。
  • 所有保存都可以在这里完成,而不是在server.writeAttachedCMinfo()。

因此,调用者包含了server.writeAttachedCMinfo() 中的一些代码:

ubiqInvoice.findById(JSON.stringify(invId).slice(1, -1), 'gwCode certifiedInvoiceAmount buyerReference supplierReference invoiceNo invoiceSerialNo invoiceFiles creditMemos')
.then(dbInvoice => {
    if(dbCreditMemo.isAssociated) {
        throw new Error('CMisAssociated');
     // ^^^^^
    }
    if(dbInvoice.gwCode === '100000000000'
        || dbInvoice.gwCode === '110000000000'
        || dbInvoice.gwCode === '111200000000'
        || dbInvoice.gwCode === '111100000000'
        || dbInvoice.gwCode === '111110000000'
    ) {
        return Promise.all(receivedObjs.arrayCMsIds.map(cmid => {
            return server.writeAttachedCMinfo(dbInvoice, cmid)
            .catch(error => {
                console.log(error);
                return null;
            });
        }))
        .then(dbCreditMemos => {
            return Promise.all(dbCreditMemos.map(memo => {
                return memo ? memo.save() : null;
            }))
            .then(() => dbInvoice.save())
            .then(() => {
                res.json(apiHelp.success(dbInvoice, 'success'));
            });
        });
    } else {
        throw new Error('wrongggwcode');
     // ^^^^^
    }
})
.catch(error => {
    console.log('error: ' + error);
    res.json(apiHelp.error(error, error));
});

捕获/处理由server.writeAttachedCMinfo() 引起的错误的整个领域需要更多思考。一方面,您可能希望保存成功的子事务并吞下错误(如上代码),而另一方面,您可能希望任何单个失败都不会导致任何内容被保存。

另一个考虑因素是server.writeAttachedCMinfo() 调用是否应该按顺序进行,这将控制子事务访问贷方余额的优先级。就目前而言,对于并行请求,它有点免费。

这比所要求的问题多一点,但希望它会有用。

【讨论】:

  • 哇...我从一位高级开发人员那里学到了这个 Promise 模式——我正在使用的那个,我现在正在阅读更多关于这个反物质的东西。那么新的 Promise 包装器是一种常见的反模式?
  • Sam,在手动承诺非承诺(例如 nodeback)方法时,需要一个承诺包装器。当 Promise 已经可用时,就像这里一样,那么你通常可以在没有 Promise 包装器的情况下做任何你想做的事情。所以当你看到new Promise(...), 时,它并不总是反模式,但经常如此。
  • 所以我认为这里很糟糕,因为诸如 -.findById 方法之类的方法已经是 Promise?
  • 是的,至少我们是这样认为的。严格来说,我们只知道该方法是“thenable”的......这很好地表明它返回了一个promise。
【解决方案2】:

在 promise 的上下文中,您实际上应该抛出错误:

throw new Error('wrongggwcode');

如果这在 promise 构造函数回调或then 回调中执行,它可以通过catch 方法(或then 的第二个参数)捕获,并且您传递给它的回调参数将是错误(对象)。

从then 回调中调用reject 显然不起作用,因为您无法访问那里的reject,但它可以在promise 构造函数回调中工作。

简单示例:

new Promise((resolve, reject) => {
    setTimeout( () => reject(new Error('this is an error')) );
}).then( (value) => console.log('resolved with ' + value) )
.catch( (error) => console.log('error message: ', error.message) );

嵌套

当您在 then 回调中嵌套了 Promise 时,请确保始终将内部 Promise 返回的值作为外部 then 回调的返回值返回。

所以在你的情况下这样做:

return creditMemo.findById( ....
//^^^^

出于同样的原因,您需要这样做:

return ubiqInvoice.findById( ....
//^^^^

这会导致这个问题/答案很远,但最好的做法是避免将 promise then 调用全部嵌套在一起。不要在嵌套的 Promise 上调用 then,只需在没有 then 调用的情况下返回 Promise,然后将 then 调用应用到更高级别,这样您就有了 then 调用的“扁平”链。这只是一个最佳实践问题,尽管只要您始终返回内部承诺,它也应该像您所做的那样工作。

错误处理程序的位置

错误处理程序放置在错误的位置;实际上,您正在使用逗号运算符。简而言之,你的代码中有这个:

new Promise(function (resolve, reject) {
    // ... //
}), function (error) {
    console.log("error: " + error);
}

逗号后面的函数永远不会执行,因为它不是方法调用的参数。它只是位于逗号运算符后面。

你想要的是在新的 Promise 上调用 catch 方法,并在其中级联错误,以便 Promise.all 也将收到拒绝:

return new Promise(function (resolve, reject) {
    // ... //
}).catch(function (error) {
    console.log("error: " + error);
    throw error; // cascade it
});

【讨论】:

  • 您好,trincto,感谢您的帖子。我确实尝试将 return new Error 更改为 'throw new Error('CMisAssociated');'但是当我针对相关的 if 条件进行测试时,我得到了一个未处理的拒绝错误
  • 确保你总是return嵌套的promise。请参阅我添加到答案中的最后一段。
  • 和'then'回调仍然抛出'return res.json(apiHelp.success(results,"success"));' ,没有发现错误。
  • 在你的代码中到处应用这个。我发现了第二次发生。请参阅我的答案。请检查所有出现此问题的情况。
  • 在针对 CMisbiggerThanInvoice 进行测试时仍然出现未处理的拒绝错误,这里代码共享中更完整的代码:codeshare.io/a3vYn1
猜你喜欢
  • 2016-08-28
  • 2021-04-01
  • 2021-06-07
  • 2019-06-19
  • 1970-01-01
  • 2018-12-20
  • 1970-01-01
  • 1970-01-01
  • 2016-08-18
相关资源
最近更新 更多