You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制Promise抛出指定自定义错误并传递至Promise.all的catch?

Why Your Promise.all Isn't Catching Errors & How to Fix It

Got it, let's break down why your Promise.all isn't picking up those custom errors, and how to fix the writeAttachedCMinfo function properly.

The Core Problem

Right now, when you hit a failed condition (like CM amount > invoice amount), you're doing return new Error('CMisbiggerThanInvoice') — but this doesn't reject the parent Promise. Those return statements are inside nested then callbacks, not in the top-level executor function of your Promise. Returning an Error object here just passes it to the next then in the chain, not the catch handler of your outer Promise.

Step-by-Step Fixes

Let's rewrite the writeAttachedCMinfo function to properly reject on failed conditions, and clean up the Promise chaining to avoid nested callbacks (which makes error handling way easier):

server.writeAttachedCMinfo = function (invId, cmid) {
  return new Promise(function (resolve, reject) {
    console.log("writeAttachedCMinfo");
    console.log("invoiceId " + invId);
    console.log("cmid " + cmid);

    // Clean up ID strings (simplified)
    const invIdString = JSON.stringify(invId).slice(1, -1);
    const cmIdString = JSON.stringify(cmid).slice(1, -1);
    const projection = 'gwCode certifiedInvoiceAmount buyerReference supplierReference invoiceNo invoiceSerialNo invoiceFiles creditMemos';

    // Use chained Promises instead of nesting for clearer flow
    ubiqInvoice.findById(invIdString, projection)
      .then(function (dbInvoice) {
        const intInvCertifiedAmount = parseInt(dbInvoice.certifiedInvoiceAmount);
        // Return the next Promise to continue the chain
        return creditMemo.findById(cmIdString).then(function (dbCreditMemo) {
          return { dbInvoice, dbCreditMemo, intInvCertifiedAmount };
        });
      })
      .then(function ({ dbInvoice, dbCreditMemo, intInvCertifiedAmount }) {
        const intCreditMemoAmount = parseInt(dbCreditMemo.creditMemoAmount);

        // Check conditions and REJECT explicitly if they fail
        if (intInvCertifiedAmount <= intCreditMemoAmount) {
          console.log('cm bigger than invoice');
          reject(new Error('CMisbiggerThanInvoice'));
          return; // Exit early to avoid unnecessary execution
        }

        if (dbCreditMemo.isAssociated) {
          reject(new Error('CMisAssociated'));
          return;
        }

        const validGwCodes = ["100000000000", "110000000000", "111200000000", "111100000000", "111110000000"];
        if (!validGwCodes.includes(dbInvoice.gwCode)) {
          reject(new Error('wrongggwcode'));
          return;
        }

        // If all checks pass, perform the database updates
        const 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;
        
        // Wait for both saves to complete before resolving
        return Promise.all([dbInvoice.save(), dbCreditMemo.save()])
          .then(() => resolve(dbInvoice));
      })
      // Catch all internal errors (queries, saves, etc.) and pass to outer Promise
      .catch(function (error) {
        console.log("error: " + error);
        reject(error);
      });
  });
};

Key Changes Explained

  1. Explicit reject() calls: Every failed condition now triggers reject(new Error('your-error-message')) — this forces the Promise into a rejected state, which will be picked up by your Promise.all catch handler.
  2. Chained Promises: Replaced nested then callbacks with a linear chain, making error propagation consistent and the code easier to read.
  3. Internal error handling: Added a top-level catch in the Promise chain to catch any errors from database queries or saves, ensuring they don't get swallowed.
  4. Wait for all saves: Used Promise.all for the two database save operations to ensure both complete successfully before resolving the outer Promise.

Your Updated Promise.all Call

Your existing Promise.all code works fine once the writeAttachedCMinfo function is fixed. You can tweak the catch handler to use the error message for cleaner output:

Promise.all(receivedObjs.arrayCMsIds.map(cmid => server.writeAttachedCMinfo(invId, cmid)))
  .then(function (results) {
    return res.json(apiHelp.success(results, "success"));
  }).catch(function (error) {
    res.json(apiHelp.error(error.message, error.message));
  });

Final Notes

  • Rule of thumb: In Promise-based code, returning an Error doesn't reject the Promise — you have to explicitly call reject().
  • Avoid deep nesting of then callbacks; chaining them keeps error handling predictable.
  • Always catch errors from all async operations (like database calls) to prevent silent failures.

内容的提问来源于stack exchange,提问作者user9601534

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:59:44