如何正确处理与IcedCoffeeScript错误?

在node.js中,通常的做法是将错误消息作为第一个参数返回给callback函数。 在纯JS(Promise,Step,Seq等)中有很多解决这个问题的方法,但是没有一个能够与ICS集成。 什么是正确的解决scheme来处理错误,而不会失去太多的可读性?

例如:

# 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 # ... 

我通过风格和编码惯例来解决这个问题。 它一直出现 让我们把你的代码片段放在下面,多做一点,以便我们有一个可行的function。

 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 

在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,这不是我最喜欢的ICSfunction,那么编译器会在你返回/短路掉函数的时候为你调用autocb。 我发现这种结构在经验上更容易出错。 例如,假设你需要在函数开始时获得一个锁,现在你需要释放它n次。 其他人可能会不同意。

另外一个说明,在下面的评论中指出。 autocbreturn autocb工作,它只接受一个值。 如果你想在这个例子中返回多个值,你需要返回一个数组或字典。 defer解构任务来帮助你在这里:

 await my_fn defer [err, image] 

正如IcedCoffeeScript存储库问题#35中所讨论的那样,还有另一种基于冰式连接器的技术 ,这些技术是将inputcallback/延期,并返回另一个callback/延期的函数。

假设你的项目有一个标准的callback参数顺序:第一个参数总是错误,成功时为空。 此外,假设您想在出现错误的第一个符号时离开函数。

第一步是做一个连接器,我打电话给“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首先创build一个my_fn (或者esc ),它的工作是双重的:(1)用错误对象触发gcb ; (2)logging有关错误发生的位置和错误的信息。 显然你应该根据你的设置改变确切的行为。 然后,所有后续调用带有callback函数的库函数将会像往常一样被defer生成callback,然后通过esc连接器运行,这将改变callback的行为。 新的行为是在错误上调用gcb全局函数,并让当前的await块成功完成。 而且,在成功的情况下,不需要处理空的错误对象,所以只填写后续的插槽(如idimagepermitted )。

这种技术是非常强大和可定制的。 关键的思想是, defer产生的callback是真正的延续,可以改变整个程序的后续控制stream程。 而且他们可以在库中这样做,这样就可以获得许多不同types的应用程序所需的错误行为,这些应用程序会调用具有不同约定的库。

Interesting Posts