当前位置: 首页 > 知识库问答 >
问题:

如何用IcedCoffeeScript正确处理错误?

鞠边浩
2023-03-14

在 node.js中,通常的做法是将错误消息作为回调函数的第一个参数返回。在纯JS中有许多解决方案(promise,步骤,seq等),但它们似乎都无法与ICS集成。在不损失太多可读性的情况下处理错误的正确解决方案是什么?

例如:

# makes code hard to read and encourage duplication
await socket.get 'image id', defer err, id
if err # ...
await Image.findById id, defer err, image
if err # ...
await check_permissions user, image, defer err, permitted
if err # ...


# will only handle the last error
await  
  socket.get 'image id', defer err, id
  Image.findById id, defer err, image
  check_permissions user, image, defer err, permitted

if err  # ...


# ugly, makes code more rigid
# no way to prevent execution of commands if the first one failed
await  
  socket.get 'image id', defer err1, id
  Image.findById id, defer err2, image
  check_permissions user, image, defer err3, permitted

if err1 || err2 || err3  # ...

共有2个答案

罗晨
2023-03-14

正如IcedCoffeeScript知识库第35期所讨论的,还有另一种基于ice风格连接器的技术,这些连接器是将回调/延迟作为输入,并返回另一个回调/延迟的函数。

假设您的项目有一个标准的回调参数顺序:第一个参数总是错误,成功时为空。此外,进一步假设您希望在出现错误的第一个符号时保留函数。

第一步是制作一个连接器,我称之为“错误短路器”或“ESC”:

{make_esc} = require 'iced-error'

其实现方式如下:

make_esc = (gcb, desc) -> (lcb) ->
    (err, args...) ->
        if not err? then lcb args...
        else if not gcb.__esc
            gcb.__esc = true
            log.error "In #{desc}: #{err}"
            gcb err

要了解它的作用,请考虑一个如何使用它的示例:

my_fn = (gcb) ->
    esc = make_esc gcb, "my_fn"
    await socket.get 'image id', esc defer id
    await Image.findById id, esc defer image
    await check_permissions user, image, esc defer permitted
    gcb null, image

这个版本的my_fn首先创建一个错误接收器(或esc),其工作是双重的:(1)用错误对象触发gcb;和(2)记录有关错误发生位置和错误内容的消息。显然,您应该根据您的设置改变确切的行为。然后,所有后续调用带有回调的库函数都将像往常一样通过 defer 生成回调,然后通过 esc 连接器运行,这将更改回调的行为。新行为是在出现错误时向函数调用 gcb 全局变量,并让当前 await 块在成功时完成。此外,在成功的情况下,无需处理空错误对象,因此仅填充后续插槽(如 id图像允许)。

这项技术非常强大且可定制。关键思想是,defer生成的回调实际上是连续的,可以改变整个程序的后续控制流。它们可以在一个库中实现这一点,因此您可以获得许多不同类型的应用程序所需的错误行为,这些应用程序调用具有不同约定的库。

邵阳德
2023-03-14

我通过风格和编码约定来解决这个问题。而且它确实一直出现。让我们在下面把你的代码段,充实一点,以便我们有一个可行的功能。

my_fn = (cb) ->
  await socket.get 'image id', defer err, id
  if err then return cb err, null
  await Image.findById id, defer err, image
  if err then return cb err, null
  await check_permissions user, image, defer err, permitted
  if err then return cb err, null
  cb err, image

你完全正确,这很难看,因为你在许多地方短路了代码,你需要记住每次返回时调用cb。

您给出的其他代码片段会产生不正确的结果,因为它们会在需要序列化的地方引入并行性。

我个人的ICS编码约定是:(1)从函数中只返回一次(控制从末端掉下来);(2)尝试在同一缩进级别处理所有错误。重写你所拥有的,以我的首选风格:

my_fn = (cb) ->
  await socket.get 'image id', defer err, id 
  await Image.findById id, defer err, image                   unless err?
  await check_permissions user, image, defer err, permitted   unless err?
  cb err, image

在套接字中发生错误的情况下。要获得调用,您需要检查错误两次,显然这两次都会失败。我不认为这是世界末日,因为它使代码更干净。

或者,您可以这样做:

my_fn = (autocb) ->
  await socket.get 'image id', defer err, id
  if err then return [ err, null ]
  await Image.findById id, defer err, image
  if err then return [ err, null ]
  await check_permissions user, image, defer err, permitted
  return [ err, image ]

如果您使用autocb,这不是我最喜欢的ICS功能,那么无论何时返回/短路函数,编译器都会为您调用autocb。根据经验,我发现这种构造更容易出错。例如,假设您需要在函数开始时获取一个锁,现在需要释放它n次。其他人可能不同意。

另一个注意事项,在下面的评论中指出。autocb的工作方式与back类似,因为它只接受一个值。如果您想像本例中那样返回多个值,则需要返回一个数组或字典。deer在这里执行解构分配以帮助您:

await my_fn defer [err, image]
 类似资料:
  • 问题内容: Java的I / O类,,,和他们的不同子类中都有一个可抛出的方法。 对于处理此类异常的正确方法是否存在共识? 我经常看到建议,只是默默地忽略它们,但这是错误的,至少在打开用于写的资源的情况下,关闭文件时出现问题可能意味着无法写入/发送未刷新的数据。 另一方面,在阅读资源时,我还不清楚为什么会抛出异常以及如何处理。 那么有什么标准建议吗? 问题答案: 记录下来。 您实际上不能 做任何事

  • 在我的REST API中,我有一个过滤器,该过滤器检查每个请求,以查看令牌是否是原样。下面是代码。 当用户登录到应用程序时,将调用上述代码。但是,令牌将在60分钟内过期。我知道,在令牌过期后,要么我必须带用户返回登录屏幕,要么刷新令牌。我把这里和这里的建议都看了一遍 但我不明白以下几点。 如何分配并将此令牌发送回用户?当前,当用户登录时,他将获得令牌并将其保存在一个变量中。为了使刷新的令牌工作,我

  • 我们有一个Hystrix(1.4.x)命令如下所示(使用Spring): 实际上,我们不想从回退方法返回(空的),而是抛出一个异常,以便的调用方知道已关闭,并可以相应地执行操作。但同时我们希望利用Hystrix提供的功能。 在我们的例子中,我们希望调用方返回一个错误消息,而不是返回一个列表。在Spring中实现了一个后退,如下所示: 从引发异常“有效”,但Hystrix将警告我们: Command

  • 我已经将我的nodejs应用程序配置为使用MongoDB。我可以成功地连接和添加数据到我的mongodb实例。我的应用程序配置如下(敏感信息编辑): 如果我点击endpoint,那么pizza文档就会很好地在数据库中创建,但我注意到连接从未关闭;这意味着endpoint从不将“all good”字符串作为响应发送。而且,对的任何后续请求都会抛出以下错误: 我似乎没有正确处理promise和asyn

  • 问题内容: 昨天我对一个问题的回答之一是建议我确保我的数据库可以正确处理UTF-8字符。我该如何使用MySQL? 问题答案: 更新: 简短答案-您几乎应该始终使用字符集和排序规则。 更改数据库: 看到: 亚伦对此答案的评论如何使MySQL正确处理UTF-8 utf8_general_ci和utf8_unicode_ci有什么区别 转换指南:https : //dev.mysql.com/doc/r

  • 问题内容: 用Java产生和使用外部进程的流(IO)的正确方法是什么?据我所知,由于可能的缓冲区大小有限,因此应在与生成进程输入并行的线程中使用java结束输入流(进程输出)。 但是我不确定我是否最终需要与这些使用者线程进行同步,或者仅等待进程退出以使用方法就足够了,以确保所有进程输出实际上都被消耗了?IE是否有可能,即使进程退出(关闭其输出流),流的Java端仍存在未读数据?实际如何知道该过程何