处理 Array#map 中的 async/await 中的错误(拒绝)

Posted

技术标签:

【中文标题】处理 Array#map 中的 async/await 中的错误(拒绝)【英文标题】:Handling Errors (Rejections) in async/await inside Array#map 【发布时间】: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(...)

【问题讨论】:

【参考方案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() 以包含整个地图。我将在问题中发布详细信息并将您的回复标记为答案,因为它完美地回答了我最初的问题。

以上是关于处理 Array#map 中的 async/await 中的错误(拒绝)的主要内容,如果未能解决你的问题,请参考以下文章

Array的 map() 和 reduce()

JavaScript Array map() 方法

array_reduce() 与 array_map()

Array.apply(null,{length:6}).map()

php中array_walk() 和 array_map()两个函数区别

JavaScript中Array内置对象里map函数的使用