【问题标题】:Handling Errors (Rejections) in async/await inside Array#map处理 Array#map 中的 async/await 中的错误(拒绝)
【发布时间】:2017-12-15 03:43:51
【问题描述】:

节点 8.1.2,我有一个结构,其中一个文件在映射中调用另一个文件的函数。在一个真实的例子中,我会在map 上使用Promise.all,但这不是这里的问题。这是结构:

A.js:

const { b } = require('./B')

function expressStuff (req, res, next) {
  things.map(thing => {
    return b(thing)
  }))

  return res.status(200).json(...)
}

B.js:

// Thing -> Promise<Object>
function b (thing) {
  return ThingModel.update(...) // this returns a Promise but FAILS and throws an errror
}

module.exports = { b }

好的。所以在函数b 中,我尝试获取一些异步数据(来自数据库)。它失败并抛出 Uncaught Promise Rejection。

如何处理?

我尝试了多种解决方案:

A1.js:

const { b } = require('./B')

function expressStuff (req, res, next) {
  things.map(thing => {
    try {
      return b(thing)
    } catch (err) {
      return next(err)
    }
  }))

  return res.status(200).json(...)
}

但这仍然没有被抓住。

A2.js:

const { b } = require('./B')

function expressStuff (req, res, next) {

  try {
    things.map(thing => {
      return b(thing)
    }))
  } catch (err) {
    return next(err)
  }

  return res.status(200).json(...)
}

仍未处理。我尝试使用Promise.all,我尝试了双重try-catch 块(因为我认为map 内部的那个可能会从next 返回到map 结果,而不是实际上来自expressStuff 函数。仍然没有。

我得到答案的最后是处理错误,但代码不会等待它被抛出,res.status()next 都可以工作,导致竞争条件和 cannot set headers after they are sent 错误。

我想做的只是让函数b 抛出一个错误,但在expressStuff 中捕获它,这样我就可以重新抛出自定义UnprocessableEntityError 并将其传递给next。似乎来自文件 B 的错误没有冒泡到调用它的 map

我该怎么做?

编辑:

我可以处理此拒绝的唯一方法是尝试在B.js 中捕获它。但是,如果我尝试重新抛出错误/返回它 - 什么都没有。错误被吞没。如果我尝试console.log 它 - 它会被记录下来。

详情:

感谢标记的答案,我重构了我的实际代码并使其完美运行。

function expressStuff (res, req, next) {
  try {
    await Promise.all(things.map(async thing => {
      if (ifSomething()) {
        await b(thing)
      }
    }))
  } catch (err) {
    return next(new MyCustomError('My Custom Error Message'))
  }

  return res.status(200).json(...)
}

【问题讨论】:

    标签: javascript node.js express asynchronous


    【解决方案1】:

    使用try/catch 处理拒绝仅适用于async functions,当您await 承诺时 - 您还没有尝试过。

    你也可以

    async function expressStuff (req, res, next) {
      var results;
      try {
        results = await Promise.all(things.map(b)); // throws when any of the promises reject
      } catch (err) {
        return next(err) // handle error
      }
      return res.status(200).json(...)
    }
    

    或(如Wait until all ES6 promises complete, even rejected promises

    function expressStuff (req, res, next) {
      const resultPromises = things.map(async (thing) => {
        try {
          return await b(thing); // throws when the promise for this particular thing rejects
        } catch (err) {
          return defaultValue; // handle error - don't call `next` here
        }
      });
      …
      return res.status(200).json(...)
    }
    

    【讨论】:

    • 啊,是的。这是一个很好的建议!但是这还是有Can't set headers after they are sent.的问题,因为return next(err)return res.status(200)都在尝试执行。有什么想法吗?
    • @TomaszGałkowski 你确定你在同一个函数中有两个返回,就像我的回答一样?
    • 是的,肯定的。这是一个异步函数,awaits 用于一件事,然后再执行该映射。在地图之后它返回 200。如果地图中发生错误,它应该返回 next 并带有我的自定义错误 - 并发送 422。它现在尝试执行此操作,感谢您。但这就是双标头的来源。
    • 你不能在map回调中调用next,它可能会在那里发生多次。如果你在next() 之后return,它不会执行res.status()。请发布导致错误的更新代码
    • 实际上,最后一条评论让我重新格式化代码并找到错误。我在map 中多了一个if 子句,所以我移动了await Promise.all() 以包含整个地图。我将在问题中发布详细信息并将您的回复标记为答案,因为它完美地回答了我最初的问题。
    猜你喜欢
    • 1970-01-01
    • 2020-04-07
    • 2020-06-24
    • 2021-10-24
    • 1970-01-01
    • 2018-09-14
    • 2017-04-23
    • 1970-01-01
    • 2018-09-28
    相关资源
    最近更新 更多