2016-12-07 1 views
1

J'ai un objet avec des instances File imbriquées dans divers emplacements. Je voudrais parcourir récursivement l'objet, vérifier si un objet est un instanceof File, utiliser une promesse de créer une URL de données à partir de l'instance, et résoudre la promesse uniquement lorsque toutes les promesses ont été résolues.Résoudre des promesses de nombres imbriquées dans un objet

J'ai une fonction existante qui renvoie une promesse et qui se résout lorsque l'URL de données du fichier est prête.

export const parsePhoto = (file) => { 
    return new Promise((resolve, reject) => { 
     const reader = new FileReader(); 

     try { 
      reader.readAsDataURL(file); 

      reader.onloadend =() => { 
       return resolve(reader.result); 
      } 
     } catch(e) { 
      console.warn('Could not upload photo', e.target); 
     } 
    }) 
} 

J'ai une fonction récursive de regarder pour un File dans l'objet.

export const convertPhotosToBase64 = (values) => { 
    if (!values) return values; 

    const converted = Object.keys(values).reduce((acc, key) => { 
     if (values[key] instanceof File) { 
      // Do something here 
      acc[key] = parsePhoto(values[key]); 
     } 

     if (isArray(values[key])) { 
      acc[key] = values[key].map(value => { 
       if (typeof value === 'object' && !isArray(value)) { 
        return convertPhotosToBase64(value); 
       } 

       return value; 
      }) 
     } 

     // Recurse if object 
     if (typeof values[key] === 'object' && !isArray(values[key])) { 
      acc[key] = convertPhotosToBase64(values[key]); 
     } 

     return acc; 
    }, values); 

    return converted; 
} 

Je veux garder la structure existante de l'objet passé (values) et remplacer uniquement les instances de fichiers avec la chaîne base64. Je connais aussi Promise.all mais je ne sais pas comment l'utiliser dans ce contexte.

Comment puis-je revenir convertPhotosToBase64 comme une promesse qui résout quand tous des fichiers ont été convertis en chaînes de base64?

+1

[ 'Promise.all'] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) – 4castle

+0

Comment serait J'utilise 'Promise.all' dans cette ins tance? Je suis conscient que 'Promise.all' existe. – Himmel

+0

Vous créez un tableau de promesses puis 'return Promise.all (promiseArray)' depuis votre gestionnaire interne '.then()'. – jfriend00

Répondre

2

Voyons d'abord simplifier votre fonction un peu, afin de réduire la duplication de toutes ces conditions:

export function convertPhotosToBase64(value) { 
    if (typeof value !== 'object') return value; 

    if (value instanceof File) return parsePhoto(value); 

    if (isArray(value)) return value.map(convertPhotosToBase64); 

    return Object.keys(value).reduce((acc, key) => { 
     acc[key] = convertPhotosToBase64(value[key]); 
     return acc; 
    }, {}); 
} 

Maintenant, parsePhoto est asynchrone et retourne une promesse. Cela signifie que l'ensemble convertPhotosToBase64 devra devenir asynchrone et toujours retourner une promesse. Compte tenu des quatre clairement des cas distincts, qui est en fait plus simple que cela puisse paraître:

export function convertPhotosToBase64(value) { 
    // wrap value 
    if (typeof value !== 'object') return Promise.resolve(value); 

    // already a promise 
    if (value instanceof File) return parsePhoto(value); 

    // map creates all the promises in parallel, use `Promise.all` to await them 
    if (isArray(value)) return Promise.all(value.map(convertPhotosToBase64)); 

    // chain one after the other 
    return Object.keys(value).reduce((accP, key) => 
     accP.then(acc => 
      convertPhotosToBase64(value[key]).then(res => { 
       acc[key] = res; 
       return acc; 
      }) 
     ) 
    , Promise.resolve({})); 
} 

Si vous êtes ok avec tout faire en parallèle (non seulement les tableaux), vous pouvez également simplifier le dernier cas à

return Object.keys(value).reduce((accP, key) => 
     Promise.all([accP, convertPhotosToBase64(value[key])]).then([acc, res] => { 
      acc[key] = res; 
      return acc; 
     }) 
    , Promise.resolve({})); 

ou peut-être mieux

const keys = Object.keys(value); 
    return Promise.all(keys.map(key => convertPhotosToBase64(value[key])).then(results => { 
     const acc = {}; 
     for (const [key, i] of keys.entries()) 
      acc[key] = results[i]; 
     return acc; 
    }); 
0

Promise.all fait ce que vous voulez. La façon la plus simple de l'utiliser dans cette situation est de faire return Promise.all(converted) au bas de votre fonction, ce qui renverra une promesse spéciale qui ne sera résolue que lorsque toutes les promesses de l'argument auront été résolues.