简体   繁体   English

NodeJs 中回调参数的最佳实践

[英]Best Practice for Callback Parameters in NodeJs

Background背景

I'm the maintainer of a low level library for fast object traversal in Node.js.我是 Node.js 中快速 object 遍历的低级库的维护者。 The focus of the library is speed and it is heavily optimised.该库的重点是速度,并且经过大量优化。 However there is one big slowdown: Callback Parameters但是有一个很大的放缓:回调参数

The Problem问题

Callbacks are provided by the library consumer and can be invoked many, many times per scan.回调由库使用者提供,每次扫描可以多次调用。 For every invocation all parameters are computed and passed to the callback.对于每次调用,都会计算所有参数并将其传递给回调。 In most cases only a fraction of the parameters are actually used by the callback.在大多数情况下,回调实际上只使用了一小部分参数。

The Goal目标

The goal is to eliminate the unnecessary computation of these parameters.目标是消除对这些参数的不必要计算。

Solutions Ideas解决方案思路

  • Ideally NodeJs would expose the callback parameters as defined by the callback.理想情况下,NodeJ 会公开回调定义的回调参数。 However obtaining them doesn't seem to be possible without a lot of black magic (string parsing).然而,如果没有大量的黑魔法(字符串解析),似乎不可能获得它们。 It would also not solve the situation where parameters are only required conditionally.它也不能解决仅有条件地需要参数的情况。
  • Instead of trying to obtain the parameters from the callback, we could require the callback to expose the required parameters.我们可以要求回调公开所需的参数,而不是尝试从回调中获取参数。 It sounds very inconvenient and error prone and would also not solve conditionally requires.这听起来很不方便且容易出错,并且也无法解决有条件的需求。
  • We could introduce a different callback for every parameter combination.我们可以为每个参数组合引入不同的回调。 This sounds like a bad idea.这听起来是个坏主意。
  • Instead of passing in the parameters directly, we could pass in a function for each parameter that computes and returns the parameter value.我们可以为每个计算并返回参数值的参数传入 function,而不是直接传入参数。 Inside the callback the parameter would then be invoked as required.在回调内部,参数将根据需要被调用。 It's ugly but might be the best approach?这很丑陋,但可能是最好的方法?

Questions问题

  • How do other libraries solve this?其他库如何解决这个问题?
  • What are other ways this can be solved?还有什么其他方法可以解决这个问题?

This is a very fundamental design decision and I'm trying to get this right.这是一个非常基本的设计决策,我正在努力做到这一点。

Thank you very much for your time!非常感谢您的宝贵时间! As always appreciated!一如既往的赞赏!

You could pass to the callback an object that has various methods on it that the client using the callback could call to fetch whatever parameters they actually need.您可以将 object 传递给回调,其中包含各种方法,使用回调的客户端可以调用以获取他们实际需要的任何参数。 That way, you'd have a clean object interface and you'd only compute the necessary information that was actually requested.这样,您将拥有一个干净的 object 接口,并且您只需计算实际请求的必要信息。

This general design pattern is sometimes called "lazy computation" where you only do the computation as required.这种通用设计模式有时被称为“惰性计算”,您只需根据需要进行计算。 You can use either accessor functions or getters, depending upon the type of interface you want to expose.您可以使用访问器函数或 getter,具体取决于您要公开的接口类型。

For performance reasons, you can perhaps reuse the same object for each time you call the callback rather than building a new one (depends upon details of your implementation).出于性能原因,您也许可以在每次调用回调时重用相同的 object 而不是构建新的回调(取决于您的实现细节)。

Note that you don't even have to put all the information needed for the computation into the object itself as the methods on the object can, in some cases, refer to your own local context and locally scoped variables when doing their computation.请注意,您甚至不必将计算所需的所有信息都放入 object 本身,因为 object 上的方法在某些情况下可以在进行计算时引用您自己的本地上下文和本地范围的变量。

However there is one big slowdown: Callback Parameters但是有一个很大的放缓:回调参数

Did you actually benchmark this?您真的对此进行了基准测试吗? I doubt constructing the argument values is that costly.我怀疑构建参数值是否昂贵。 Notice that if this is a really heavily used call, V8 might be able to inline it and then optimise away unused argument values.请注意,如果这是一个非常频繁使用的调用,V8 可能能够内联它,然后优化掉未使用的参数值。

Ideally NodeJs would expose the callback parameters as defined by the callback.理想情况下,NodeJ 会公开回调定义的回调参数。

Actually, it does .实际上, 确实如此 If you do want to rely on this property though, you should properly document that you do, otherwise this magic could lead to obscure bugs.如果你确实想依赖这个属性,你应该正确地记录你所做的,否则这个魔法可能会导致模糊的错误。

We could introduce a different callback for every parameter combination.我们可以为每个参数组合引入不同的回调。 This sounds like a bad idea.这听起来是个坏主意。

It doesn't seem to be that much of a problem to provide two options, filter(key, value) and filterDetailed(key, value, context) .提供两个选项filter(key, value)filterDetailed(key, value, context)似乎不是什么大问题。 If the optimisation is really worth it, and as you say this is a low-level library, just go for it.如果优化真的值得,并且正如您所说,这是一个低级库,只需 go 即可。

Instead of passing in the parameters directly, we could pass in a function for each parameter that computes and returns the parameter value.我们可以为每个计算并返回参数值的参数传入 function,而不是直接传入参数。 Inside the callback the parameter would then be invoked as required.在回调内部,参数将根据需要被调用。 It's ugly but might be the best approach?这很丑陋,但可能是最好的方法?

Constructing a closure object to pass instead of a parameter does have some overhead as well, so you will need to benchmark this properly.构造一个闭包 object 来传递而不是传递参数确实也有一些开销,因此您需要正确地进行基准测试。 It might not be worth it.这可能不值得。

However, I see that you are actually passing a single context object as the argument on which the computed values are accessed as properties.但是,我看到您实际上将单个上下文 object 作为参数传递,计算值在该参数上作为属性访问。 In that case, you can simply make these properties getters that will compute the value when they are accessed, not when the object is constructed.在这种情况下,您可以简单地使这些属性获取器在访问它们时计算值,而不是在构造 object 时计算值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM