1
votes

Comment effectuer une transformation asynchrone mappée d'une structure imbriquée?

Disons que j'ai du code qui ressemble à ceci:

{
   outerList : [
      {
         innerList: [
            2, 3, 4
         ]
      }, 
      {
         innerList: [
            3, 5, 7
         ]
      }
   ]
}; 

Et je veux transformer l'objet en ceci:

const myObject = {
   outerList : [
      {
         innerList: [
            1, 2, 3
         ]
      }, 
      {
         innerList: [
            2, 4, 6
         ]
      }
   ]
}; 

async function asyncTransform(i) {  
    return new Promise((resolve) => {
      setTimeout(() => {
          resolve(i+1); 
      }, Math.random()* 1000); 
    }); 
}

async function asyncTransformNestedObject(obj) {
   //??? 
}

asyncTransformNestedObject(myObject).then((result) => {
    console.log(result);
}); 

Quelle serait la meilleure façon de faire cela - idéalement d'une manière où l'async fonctionne exécuter simultanément.


2 commentaires

avons-nous vraiment besoin d'une fonction asynchrone ici?


@brk Oui. C'est juste un exemple. Dans la vraie vie, ce sont des appels API.


3 Réponses :


1
votes
  • Array.map chaque élément interne à une promesse renvoyée par asyncTransform , puis transmettez ce tableau à Promise.all .
  • Puis Promise.all chaque Promise.all créé à l'étape 1.
  • Voici un exemple:

    const myObject = {
      outerList : [
        {
          innerList: [
            1, 2, 3
          ]
        }, 
        {
          innerList: [
            2, 4, 6
          ]
        }
      ]
    }
    
    function asyncTransform(i) {
      return new Promise(resolve => setTimeout(() => resolve(i + 1), 50))
    }
    
    function asyncTransformNestedObject(obj) {    
      const innerLists = obj.outerList.map(el => {
        return Promise.all(el.innerList.map(asyncTransform))
          .then(results => el.innerList = results)
      })
      
      return Promise.all(innerLists)
        .then((results, i) => obj.outerList.map((el, i) => ({ 
            ...el, 
            innerList: results[i] 
          })))
    }
    
    
    asyncTransformNestedObject(myObject).then((result) => {
      console.log(result)
    })

2 commentaires

Notez que vous ne reproduisez pas correctement la structure de l'objet d'origine.


C'est vrai, a ajouté un correctif.



0
votes

Voici la solution avec laquelle j'ai fini par aller:

C'est assez simple quand on y pense:

Un Promise.all of a Promise.all va se résoudre au moment maximum de toutes les promesses.

const myObject = {
   outerList : [
      {
         innerList: [
            1, 2, 3
         ]
      }, 
      {
         innerList: [
            2, 4, 6
         ]
      }
   ]
}; 

async function asyncTransform(i) {  
    return new Promise((resolve) => {
      setTimeout(() => {
          resolve(i+1); 
      }, Math.random()* 1000); 
    }); 
}

async function transformInnerObj(innerObj) {    
    const newInnerList = await Promise.all(innerObj.innerList.map(i => asyncTransform(i))); 
    return {
      innerList: newInnerList
    }; 
}

async function asyncTransformNestedObject(obj) {
   
   const newOuterList = await Promise.all(obj.outerList.map(innerObj =>       transformInnerObj(innerObj)));                                           
   return {
      outerList:  newOuterList                                  
   }; 
}

asyncTransformNestedObject(myObject).then(result => {
    console.log(result);
});


1 commentaires

Mineure: les fonctions qui renvoient explicitement une Promise , comme asyncTransform n'ont pas besoin d'être marquées comme async .



0
votes

Je résoudrais cela en utilisant un setTimeout / requestAnimationFrame récursif dans ES5, mais si vous insistez sur la fonction async, cela semble faire l'affaire:

     async function convert(arr,i = 0){
            if(!arr[i]){return arr}
            await arr[i].innerList.reduce((ac,d,i,a) => ++a[i],(async function(){}()));
            return convert(arr,++i);
        }

    convert(myObject.outerList);

//myObject
    "[
        {
            "innerList": [
                2,
                3,
                4
            ]
        },
        {
            "innerList": [
                3,
                5,
                7
            ]
        }
    ]"

Vous n'avez rien spécifié sur la mutation l'objet d'origine, donc je l'ai changé en place. J'ai également renvoyé le innerArray, vous auriez pu renvoyer l'objet lui-même instean et utiliser await pour stocker dans une variable.


2 commentaires

Ce n'est pas mon vote négatif, mais c'est un code assez cryptique.


@dwjohnston, je comprends, np. C'est pourquoi j'ai dit que je résoudrais avec ES5 récursif. J'ai vu que vous mettiez un peu l'accent sur les fonctions async, je me suis donc demandé s'il serait possible d'utiliser uniquement async / await. La partie de réduction consiste à forcer la ligne à renvoyer une promesse résolue avec undefined et à l'attendre pour que tous les éléments du tableau soient incrémentés, si c'est la partie qui était cryptique.