Управление функциями синхронизации и асинхронности в стеке вызовов алгоритмов

#javascript #node.js #asynchronous #design-patterns #callstack

Вопрос:

TLDR: должен ли я использовать async и await использовать сложный стек вызовов, если большинство функций на самом деле не async являются таковыми ? Существуют ли альтернативные шаблоны программирования?

Контекст

Этот вопрос, вероятно, больше касается шаблонов проектирования и общей архитектуры программного обеспечения, чем конкретной проблемы синтаксиса. Я пишу алгоритм в node.js это довольно сложно. Поток программы включает в себя начальный асинхронный вызов для извлечения некоторых данных, а затем переходит к серии синхронных шагов вычисления на основе данных. Шаги вычисления являются итеративными, и по мере их выполнения они генерируют результаты. Но иногда, если при расчетах выполняются определенные условия, потребуется получить дополнительные данные. Вот упрощенная версия в виде диаграммы:

введите описание изображения здесь

calcStep Цикл выполняется тысячи раз синхронно, подталкивая к результатам. Но иногда он возвращается getData , и программе приходится ждать поступления дополнительных данных, прежде чем снова переходить к calcStep циклу.

В коде

Упрощенная версия вышесказанного может выглядеть так в коде JS:

 let data;

async function init() {
  data = await getData();
  processData();
  calcStep1();
}

function calcStep1() {
  // do some calculations
  calcStep2();
}

function calcStep2() {
  // do more calculations
  calcStep3();
}

function calcStep3() {
  // do more calculations
  pushToResults();

  if (some_condition) {
    getData(); // <------ question is here
  }

  if (stop_condition) {
    finish();
  } else {
    calcStep1();
  }
}
 

Где pushToResults и finish также являются простыми синхронными функциями. Я пишу calcStep , что функции здесь разделены, потому что в реальном коде они на самом деле являются методами классов из классов, определенных на основе разделения проблем.

Проблема

Очевидная проблема возникает, если some_condition выполняется, и мне нужно ждать, чтобы получить больше данных, прежде чем продолжить calcStep цикл, я должен использовать await ключевое слово перед вызовом getData в calcStep3 , Что означает, что calcStep3 должен быть вызван async , и мы должны await что также В calcStep2 , и всю дорогу вверх по цепочке, даже синхронные функции должны быть обозначены async и await Эд.

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

Ответ №1:

Если вы не хотите создавать функцию async и распространять ее по цепочке, используйте .then() . Вам нужно будет продублировать следующий код внутри .then() ; вы можете упростить это, поместив его в собственную функцию.

 function maybeRepeat() {
  if (stop_condition) {
    finish();
  } else {
    calcStep1();
  }
}

function calcStep3() {
  // do more calculations
  pushToResults();

  if (some_condition) {
    getData().then(maybeRepeat);
  } else {
    maybeRepeat()
  }
}