Преобразование javascript l oop в асинхронный и функциональный - PullRequest
1 голос
/ 18 марта 2020

Я пытаюсь сделать следующий код более функциональным и асинхронным, но без особой удачи!

Поэтому вызовы API могут принимать только 5 идентификаторов за раз, поэтому я и создал splitListOfIds и зацикливаюсь на нем. Второй вызов API зависит от первого вызова API

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

const splitListOfIds = [
    [1,2,3,4,5],
    [6,7,8,9,10]
]

const allPolicies = []

for (let i = 0; i < splitListOfIds.length; i++) {
    const people = await peopleApi.getPeople(splitListOfIds[i])
    const policyIds = people.map(p => p.policyId)
    const policies = await policyApi.getPolicyDetails(policyIds)
    allPolicies.push(policies)
}

Ответы [ 3 ]

3 голосов
/ 18 марта 2020

Я бы использовал Rx Js и Observable , чтобы сделать это элегантным способом, например

// Emit people ids as a sequence
const source = Rx.Observable.from([1,2,3,4,5,6,7,8,9]);
const subscribe = source
.bufferCount(5) // Buffer people ids to ensure the size won't exceed 5 for api calls
.flatMap(peopleIds => peopleApi.getPeople(peopleIds))
.map(people => people.map(user => user.policyId))
.flatMap(policyIds => policyApi.getPolicyDetails(policyIds))
.subscribe(policies => console.log(policies));

Вот фрагмент, высмеивающий ваш API

const mockPeople = [
  { "id": 1, "name": "Jack", "policyId": "p-01" },
  { "id": 2, "name": "Isobel", "policyId": "p-02" },
  { "id": 3, "name": "Steve", "policyId": "p-03" },
  { "id": 4, "name": "James", "policyId": "p-04" },
  { "id": 5, "name": "Marty", "policyId": "p-05" },
  { "id": 6, "name": "Janis", "policyId": "p-06" },
  { "id": 7, "name": "Annabel", "policyId": "p-07" },
  { "id": 8, "name": "Flora", "policyId": "p-08" },
  { "id": 9, "name": "Richard", "policyId": "p-09" },
]

const mockPolicies = [
  { "id": "p-01", "details": "Details for Jack's policy" },
  { "id": "p-02", "details": "Details for Isobel's policy" },
  { "id": "p-03", "details": "Details for Steve's policy" },
  { "id": "p-04", "details": "Details for James's policy" },
  { "id": "p-05", "details": "Details for Marty's policy" },
  { "id": "p-06", "details": "Details for Janis's policy" },
  { "id": "p-07", "details": "Details for Annabel's policy" },
  { "id": "p-08", "details": "Details for Flora's policy" },
  { "id": "p-09", "details": "Details for Richard's policy" }  
]

let mockGetPeople = async (peopleIds) => {
	let filteredPeople = mockPeople.filter(user => {
  	return peopleIds.indexOf(user.id) !== -1;
  });
	return Promise.resolve(filteredPeople);
}
let mockGetPolicies = async (policyIds) => {
	let filteredPolicies = mockPolicies.filter(policy => {
  	return policyIds.indexOf(policy.id) !== -1;
  });
	return Promise.resolve(filteredPolicies);
}


const source = Rx.Observable.from([1,2,3,4,5,6,7,8,9]);
const subscribe = source
.bufferCount(5)
.flatMap(peopleIds => mockGetPeople(peopleIds)) // mock of peopleApi.getPeople
.map(people => people.map(user => user.policyId))
.flatMap(policyIds => mockGetPolicies(policyIds)) // mock of policyApi.getPolicyDetails
.subscribe(policies => console.log(policies));
<script src="https://npmcdn.com/@reactivex/rxjs@5.0.0-beta.8/dist/global/Rx.umd.js"></script>
2 голосов
/ 18 марта 2020

Если этот API позволяет вам выполнять запросы параллельно, вы можете использовать такой подход:

const allPolicies = await Promise.all(splitListOfIds.map(async peopleIds => {
  const people = await peopleApi.getPeople(peopleIds)
  const policyIds = people.map(p => p.policyId)
  const policies = await policyApi.getPolicyDetails(policyIds)
  return policies;
}));

В зависимости от того, что API делают в фоновом режиме, это может привести к неприятностям, если вы У меня слишком много вещей параллельно. Если это так, то вам нужно реализовать какой-то механизм максимального параллелизма, что-то вроде этого (не проверено, хотя, вероятно, уже есть библиотеки для этого):

async function processInParallel(maxParallelism, data, taskFn) {
  const results = [];
  const inFlight = new Set();  

  for (let i = 0; i < data.length; i++) {
    while (inFlight.size >= maxParallelism) {
      // Wait for at least one to complete
      await Promise.race([...inFlight]);
    } 

    const task = taskFn(data[i]).then(result => {
      results.push(result);
      inFlight.delete(task);
    });
    inFlight.add(task);
  }

  await Promise.all([...inFlight]);

  return results;
}

// Usage
const allPolicies = await processInParallel(10, splitListOfIds, async peopleIds => {
  const people = await peopleApi.getPeople(peopleIds)
  const policyIds = people.map(p => p.policyId)
  const policies = await policyApi.getPolicyDetails(policyIds)
  return policies;
}));
1 голос
/ 18 марта 2020

Вы должны map каждый асин c вызвать Обещание, а затем ждать их всех вместе, вот так:

async function callAsync() {

    const asyncCalls = splitListOfIds.map(async function(pieceOfListOfIds) {

        const people = await peopleApi.getPeople(pieceOfListOfIds);
        const policyIds = people.map(p => p.policyId);
        const policies = await policyApi.getPolicyDetails(policyIds);

        allPolicies.push(policies);

    });

    await Promise.all(asyncCalls);
}

В основном каждый async function возвращает Promise, что вы будете собрать в массиве asyncCalls и затем с помощью Promise.all дождаться завершения каждого обещания.
Если вы просто используете forEach, ваша программа продолжит выполнение, не ожидая различных обещаний.

РЕДАКТИРОВАТЬ:

Примечание: этот код будет выполнять все вызовы API в параллельно

...