2016-12-07 13 views
1

У меня есть объект с вложенными File экземплярами в разных местах. Я хотел бы рекурсивно пройти через объект, проверить, является ли объект instanceof File, использовать обещание создать URL-адрес данных из экземпляра и разрешить обещание только тогда, когда все обещания были разрешены.Разрешить любое число обещаний, вложенных внутри объекта

У меня есть существующие функции, которые возвращают Promise и решает, когда URL-адрес данных из файла будет готов.

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

У меня есть функция рекурсивно искать для File в объекте.

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

Я хочу, чтобы сохранить существующую структуру объекта, переданного (values) и заменить только экземпляры файлов со строкой base64.

Я также знаю Promise.all, но не знаю, как его использовать в этом контексте.

Как я могу вернуть convertPhotosToBase64 как обещание, устраняющее когда все файлы были преобразованы в base64 строки?

+1

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

+0

Как бы Я использую 'Promise.all' в этом стояние? Я знаю, что 'Promise.all' существует. – Himmel

+0

Вы создаете массив обещаний, а затем 'возвращаете Promise.all (promarray)' из вашего внутреннего обработчика '.then()'. – jfriend00

ответ

2

Давайте сначала упростить функцию немного, чтобы уменьшить дублирование всех этих условий:

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

Теперь parsePhoto асинхронный и возвращает обещание. Это означает, что весь convertPhotosToBase64 должен стать асинхронным и всегда возвращать обещание. Учитывая четыре четко выраженные случаи, что на самом деле проще, чем это звучит:

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

Если вы нормально с делать все параллельно (а не только массивы), вы также можете упростить последний случай с

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

или, может быть, лучше

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 делает то, что вы хотите. Самый простой способ использовать его в этой ситуации - сделать return Promise.all(converted) внизу вашей функции, которая вернет особое обещание, которое не будет разрешено, пока все обещания в аргументе не будут решены.