1
votes

Conversion d'une boucle javascript pour qu'elle soit asynchrone et fonctionnelle

J'essaie de rendre le code suivant plus fonctionnel et asynchrone, mais je n'ai pas beaucoup de chance!

Les appels API ne peuvent donc prendre que 5 identifiants à la fois, c'est pourquoi j'ai créé splitListOfIds et je suis en train de le parcourir.Le deuxième appel de l'API dépend du premier appel de l'API

Le code ci-dessous fonctionne, mais il est lent et j'obtiens des erreurs ESLINT avec ce code et je veux le faire plus fonctionnel et asynchrone. J'ai essayé d'utiliser Promises mais je ne sais pas comment le faire. Idéalement, vous souhaitez vous débarrasser de la boucle for .

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)
}


8 commentaires

zellwk.com/blog/async-await-in-loops


(Vous utilisez déjà des promesses btw)


Si vous utilisez wait , vous utilisez déjà Promises. Une raison spécifique pour laquelle vous souhaitez vous débarrasser de la boucle for?


Qu'entendez-vous par «plus asynchrone»?


Quelles erreurs de linter obtenez-vous?


Lorsque vous dites «ne peut prendre que 5 identifiants à la fois», cela signifie-t-il que l'appel n'accepte que 5 identifiants à la fois mais que vous êtes autorisé à faire des lots de 5 en parallèle?


eslint me donne des erreurs à ce sujet ... dit pas d'attente dans une boucle. De plus, une boucle for n'est pas asynchrone, n'est-ce pas ?? contrairement à forEach qui est asynchrone?


oui, l'api n'accepte qu'un lot de 5 à la fois


3 Réponses :


2
votes

Si cette API vous permet de faire des requêtes en parallèle, vous pouvez adopter ce type d'approche:

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;
}));

En fonction de ce que font les API en arrière-plan, cela peut vous causer des problèmes si vous avez trop de choses en parallèle. Si tel est le cas, vous devez implémenter une sorte de mécanisme de parallélisme maximal, quelque chose comme celui-ci (non testé, bien qu'il existe probablement déjà des bibliothèques pour cela):

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;
}));


1 commentaires

cela semble prometteur! laissez-moi l'essayer et acceptera votre réponse si cela fait l'affaire



1
votes

Vous devez mapper chaque appel asynchrone à une promesse puis les attendre tous ensemble, comme ceci:

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);
}

En gros, chaque fonction asynchrone code> renvoie une Promise que vous collecterez dans le tableau asyncCalls , puis avec Promise.all vous attendez que chaque promesse soit réglée.
Si vous utilisez simplement forEach , votre programme continuera l'exécution sans attendre les différentes promesses.

EDIT:

Remarque: ce code exécutera tous les appels d'API en parallèle


2 commentaires

Corrigez-moi si je me trompe, mais il n'y a pas de différence fonctionnelle entre le vôtre et Jacobs, n'est-ce pas?


Oui, Jacob a posté pendant que j'écrivais. Si cette solution répond à vos besoins, veuillez accepter la réponse de Jacob, car il est arrivé le premier.



3
votes

J'utiliserais RxJs et Observable pour le faire de manière élégante, par exemple

<script src="https://npmcdn.com/@reactivex/rxjs@5.0.0-beta.8/dist/global/Rx.umd.js"></script>

Voici un extrait de code se moquant de votre API strong >

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));
// 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));


0 commentaires