Лучшая практика для параметров обратного вызова в NodeJs - PullRequest
0 голосов
/ 04 апреля 2020

Фон

Я поддерживаю низкоуровневую библиотеку для быстрого обхода объекта в Node.js. В центре внимания библиотеки - скорость, и она сильно оптимизирована. Однако есть одно большое замедление: Параметры обратного вызова

Проблема

Обратные вызовы предоставляются потребителем библиотеки и могут быть вызваны много, много раз за скан. Для каждого вызова все параметры вычисляются и передаются обратному вызову. В большинстве случаев только часть параметров фактически используется обратным вызовом.

Цель

Цель состоит в том, чтобы устранить ненужные вычисления этих параметров.

Идеи решений

  • В идеале NodeJs представит параметры обратного вызова, как определено обратным вызовом. Однако получить их, кажется, невозможно без большого количества черных магов c (разбор строк). Это также не решило бы ситуацию, когда параметры требуются только условно.
  • Вместо того, чтобы пытаться получить параметры из обратного вызова, мы могли бы потребовать, чтобы обратный вызов предоставил требуемые параметры. Это звучит очень неудобно и подвержено ошибкам, а также не решает условно.
  • Мы могли бы ввести различный обратный вызов для каждой комбинации параметров. Это звучит как плохая идея.
  • Вместо прямой передачи параметров, мы можем передать функцию для каждого параметра, который вычисляет и возвращает значение параметра. Внутри обратного вызова параметр будет вызываться по мере необходимости. Это некрасиво, но может быть лучшим подходом?

Вопросы

  • Как другие библиотеки решают эту проблему?
  • Какие другие Как это можно решить?

Это очень фундаментальное проектное решение, и я пытаюсь сделать это правильно.

Большое спасибо за потраченное время! Как всегда ценится!

Ответы [ 2 ]

1 голос
/ 04 апреля 2020

Однако есть одно существенное замедление: параметры обратного вызова

Вы действительно тестировали это? Я сомневаюсь, что построение значений аргумента - это дорого. Обратите внимание, что если это действительно интенсивно используемый вызов, V8 может включить его и затем оптимизировать неиспользуемые значения аргумента.

В идеале NodeJs представит параметры обратного вызова, как определено обратным вызовом.

На самом деле, это . Однако если вы действительно хотите полагаться на это свойство, вам следует правильно задокументировать, что вы делаете, в противном случае это волшебство c может привести к неясным ошибкам.

Мы могли бы ввести различный обратный вызов для каждой комбинации параметров , Это звучит как плохая идея.

Похоже, не так уж и сложно обеспечить два варианта: filter(key, value) и filterDetailed(key, value, context). Если оптимизация действительно того стоит, и, как вы говорите, это библиотека низкого уровня, просто для нее go.

Вместо прямой передачи параметров, мы могли бы передать функцию для каждого параметра, который вычисляет и возвращает значение параметра. Внутри обратного вызова параметр будет вызываться по мере необходимости. Это уродливо, но может быть лучшим подходом?

Построение объекта замыкания для передачи вместо параметра также имеет некоторые накладные расходы, поэтому вам нужно будет правильно его сравнить. Возможно, это того не стоит.

Однако я вижу, что вы фактически передаете один объект контекста в качестве аргумента, к которому к вычисленным значениям обращаются как к свойствам. В этом случае вы можете просто сделать эти свойства получателями, которые будут вычислять значение при обращении к ним, а не при создании объекта.

1 голос
/ 04 апреля 2020

Вы могли бы передать обратному вызову объект, имеющий различные методы, которые клиент, использующий обратный вызов, мог бы вызывать для получения любых параметров, которые ему действительно нужны. Таким образом, у вас будет чистый объектный интерфейс, и вы будете вычислять только ту информацию, которая была запрошена.

Этот общий шаблон проектирования иногда называют «ленивым вычислением», когда вы выполняете вычисления только по мере необходимости. Вы можете использовать как функции доступа, так и методы получения, в зависимости от типа интерфейса, который вы хотите предоставить.

По соображениям производительности вы, возможно, можете повторно использовать один и тот же объект при каждом вызове обратного вызова, а не создавать новый. (зависит от деталей вашей реализации).

Обратите внимание, что вам даже не нужно помещать всю информацию, необходимую для вычисления, в сам объект, так как в некоторых случаях методы объекта могут ссылаться на ваш собственный локальный контекст и переменные локальной области видимости при выполнении их вычисления.

...