Async await многое сделал для улучшения читаемости кода по сравнению со стилем обратных вызовов. Но иногда это не очень хорошая конструкция, особенно если вы хотите использовать ее в функциональном стиле!!!

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

Каждый раз, когда мы используем await, будет создаваться асинхронная ветка. Результатом будет поток выполнения в форме дерева, в котором некоторые цепные функции выполняются последовательно, а другие — одновременно.

В библиотеке js-awe есть функция «план», которая может помочь вам в этом.

«plan» пытается решить эту проблему, явно объявив это дерево в одном месте и простым элегантным способом. Он использует вложенность массива для определения этого дерева. Он не использует странный DSL. «plan» — это планировщик выполнения, который передает функции для последовательного запуска и функции для одновременного выполнения. Он обрабатывает для вас цепочку обещаний и передачу данных **, поэтому вы можете писать чистые функции без асинхронного ожидания **.

Конструкция для последовательного запуска:

[ fun1, fun2, fun3 ]

execution and data flow coincides:

fun1 -> fun2 -> fun3

* fun1 receives all the parameters when running the plan:
   plan().build(...)(param1, param2)
* fun2 receives output from fun1
* fun3 receives output from fun2

Конструкция запускается одновременно:

[ fun1, [fun2], [fun3], fun4 ]

execution and data flow coincides:

       |-> fun2 --|
fun1 --|          |-> fun4
       |-> fun3 --|

* fun1 receives all the parameters when running the plan:
   plan().build(...)(param1, param2)
* fun2 receives output from fun1
* fun3 receives output from fun1
* fun4 receives an array that contains two values:
  [outputFromfun2, outputFromFun3]

Лучше всего посмотреть пример. Во-первых, нам нужно установить его:

npm install js-awe

Ниже приведен простой пример его использования. Это может быть частью API для получения банковских балансов всех активов (сбережений и кредитов) для конкретного клиента:

import { plan } from 'js-awe'

const getCustomerBalances = plan().build([
  fetchAccounts,
  [filterSavings, getSavingBalances],
  [filterLoans, getLoanBalances],
  format,
])

console.log('result: ', await getCustomerBalances('0396d9b0'))

Выполнение:

            |->filterSavings -> getSavingBalances -|
getAccounts-|                                      |-> format
            |->filterLoans   -> getLoanBalances   -|

Поток данных:

- Возвращаемые значения из функций передаются следующей функции для запуска в стиле конвейера.

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

Вы можете увидеть весь пример здесь:

import { plan } from 'js-awe'

const getCustomerBalances = plan().build([
  fetchAccounts,
  [filterSavings, getSavingBalances],
  [filterLoans, getLoanBalances],
  format,
])

console.log('result: ', await getCustomerBalances('0396d9b0'))

function filterSavings(accounts) {
  return accounts.filter((account) => account.type === 'saving')
}

function getSavingBalances(savingAccounts) {
  const listOfAcccountsToFetch = savingAccounts.map((account) => account.id)
  return fetchSavingBalances(listOfAcccountsToFetch)
}

function filterLoans(accounts) {
  return accounts.filter((account) => account.type === 'loan')
}
function getLoanBalances(loanAccounts) {
  const listOfAcccountsToFetch = loanAccounts.map((account) => account.id)
  return fetchLoanBalances(listOfAcccountsToFetch)
}

function format([savingBalances, loanBalances]) {
  return [...savingBalances, ...loanBalances]
}

// Data fetch services are mocked for local running.
// In production they should be fetch APIs to real implementations.
function fetchAccounts(customerId) {
  return Promise.resolve([
    { id: 1, type: 'saving' },
    { id: 2, type: 'loan' },
  ])
}

function fetchSavingBalances(listOfAcccountsToFetch) {
  return Promise.resolve([
    {
      id: 1,
      type: 'saving',
      balance: 13,
    },
  ])
}

function fetchLoanBalances(listOfAcccountsToFetch) {
  return Promise.resolve([
    {
      id: 2,
      type: 'loan',
      balance: 24,
    },
  ])
}

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

Когда не рекомендуется:

  • Простые асинхронные потоки. Представлять еще один инструмент для обучения, возможно, не стоит.
  • У вас есть навыки, чтобы сделать это лучше самостоятельно: более гибко, читабельно и производительно.
  • Вы устали от новых библиотек, фреймворков и абстракций. Я понимаю!
  • Вы довольны своим нынешним подходом.