【问题标题】:How to correctly handle errors with IcedCoffeeScript?如何正确处理 IcedCoffeeScript 的错误?
【发布时间】:2012-12-28 15:03:51
【问题描述】:

node.js 中的常见做法是将错误消息作为第一个参数返回给回调函数。在纯 JS(Promise、Step、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  # ...

【问题讨论】:

    标签: node.js error-handling coffeescript iced-coffeescript


    【解决方案1】:

    我通过样式和编码约定解决了这个问题。它确实一直出现。让我们把你的 sn-p 放在下面,再充实一点,这样我们就有了一个可行的功能。

    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。

    您提供的其他 sn-ps 会产生不正确的结果,因为它们会在需要序列化的地方引入并行性。

    我个人的 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
    

    在socket.get调用出错的情况下,需要检查两次错误,显然两次都会失败。我不认为这是世界末日,因为它使代码更干净。

    或者,您可以这样做:

    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 次。其他人可能不同意。

    另一个注释,在下面的 cmets 中指出。 autocb 的工作方式与 return 类似,因为它只接受一个值。如果你想像这个例子一样返回多个值,你需要返回一个数组或字典。 defer 进行解构分配以帮助您:

    await my_fn defer [err, image]
    

    【讨论】:

    • 谢谢,由于某种原因,并行运行这种顺序命令对我有用,我会在我这边解决这个问题,似乎'...除非'是一个足够好的解决方案。另外关于我以前没有听说过该功能的 autocb,它记录在哪里?
    • 谢谢,最好在自述文件的某处有一个指向该文档的链接,因为它仍然指向稍微过时的maxtaco.github.com/coffee-script 页面
    • autocb 不适用于 Node 样式的回调。您只能返回一个值。上面的代码会产生错误。见github.com/maxtaco/coffee-script/issues/29
    • 你是对的,感谢您指出这一点。我通过返回对“修复”了这个例子。
    【解决方案2】:

    正如 IcedCoffeeScript 存储库的 Issue #35 中所讨论的,还有另一种基于 iced 样式的连接器 的技术,这些函数将回调/延迟作为输入,并返回另一个回调/延迟。

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

    第一步是制作一个连接器,我称之为“ErrorShortCircuiter”或“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首先创建一个ErrorShortCircuiter(或esc),它的工作是双重的:(1)用错误对象触发gcb; (2) 记录有关错误发生的位置和错误内容的消息。显然,您应该根据您的设置改变确切的行为。然后,所有后续对带有回调的库函数的调用都会像往常一样得到defer 生成的回调,然后通过esc 连接器运行,这将改变回调的行为。新行为是在出错时调用函数gcb 全局,并让当前await 块在成功时完成。另外,在成功的情况下,不需要处理空错误对象,因此只填充后续槽(如idimagepermitted)。

    这种技术非常强大且可定制。关键思想是defer产生的回调是真正的延续,可以改变整个程序的后续控制流。他们可以在库中执行此操作,因此您可以获得许多不同类型的应用程序所需的错误行为,这些应用程序调用具有不同约定的库。

    【讨论】:

    • 查看我对这个问题的评论。你的错误必须是一个错误对象,而不是一个普通的字符串。
    猜你喜欢
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    • 2018-12-06
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    相关资源
    最近更新 更多