Как использовать Promise.all в следующем коде readFile? - PullRequest
0 голосов
/ 06 сентября 2018

В следующем коде я читаю некоторые файлы и получаю их имена и текст. После этого я сохраняю данные в переменной option для создания файла epub:

const Epub = require("epub-gen")
const folder = './files/'
const fs = require('fs')
let content = []

fs.readdir(folder, (err, files) => {
  files.forEach(filename => {
    const title = filename.split('.').slice(0, -1).join('.')
    const data = fs.readFileSync(`${folder}${filename}`).toString('utf-8')
    content.push({ title, data })
  })
})

const option = {
  title: "Alice's Adventures in Wonderland", // *Required, title of the book.
  content
}

new Epub(option, "./text.epub")

Проблема в том, что new Epub запускается до того, как файлы будут прочитаны, до того, как content будет готово. Я думаю, что Promise.all является подходящим кандидатом здесь. Я проверил документы Mozilla . Но он показывает различные обещания в качестве примера, но у меня нет ни одного. Так что я не совсем уверен, как использовать Promise.all здесь.

Любой совет?

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Добавить обещания в массив. Каждое обещание должно соответствовать значению, которое вы вводите в content

Когда все обещания разрешатся, возвращаемое значение будет массивом, ранее известным как content.

Кроме того, вы можете и должны использовать все асинхронные fs вызовы. Так что readFileSync можно заменить на readFile (асинхронно). Однако я не заменил ваш код этим асинхронным вызовом, чтобы вы могли четко видеть, что требовалось для ответа на исходный вопрос.

Не уверен, правильно ли я разместил фрагмент кода.

const Epub = require("epub-gen")
const folder = './files/'
const fs = require('fs')

let promises = []

fs.readdir(folder, (err, files) => {
  files.forEach(filename => {
    promises.push(new Promise((resolve, reject) => {

      const title = filename.split('.').slice(0, -1).join('.')
      const data = fs.readFile(`${folder}${filename}`).toString('utf-8')
      resolve({
        title,
        data
      })

    }))
  })
})

const option = {
  title: "Alice's Adventures in Wonderland", // *Required, title of the book.
  content
}

new Epub(option, "./text.epub")

Promise.all(promises).then((content) => {
  //done
})
0 голосов
/ 06 сентября 2018

На данный момент вы можете делать все синхронно, так как вы используете readFileSync. Таким образом, вы можете поместить создание Epub после цикла forEach.

Если вы хотите сделать асинхронный, мой первый вопрос будет:

Поддерживает ли ваша версия node.js util.promisify (версия узла 8.x или выше iirc)? Если это так, это можно использовать для превращения функций обратного вызова, таких как readFile, и тому подобных в обещания. Если нет, вы можете использовать ту же логику, но затем с вложенными обратными вызовами, как показано в других решениях.

const FS = require( 'fs' );
const { promisify } = require( 'util' );
const readFile = promisify( FS.readFile );
const readFolder = promisify( FS.readFolder );

readFolder( folder )
  // extract the file paths. Maybe using `/${filename}` suffices here.
  .then( files => files.map( filename => `${folder}${filename}`))
  // map the paths with readFile so we get an array with promises.
  .then( file_paths => file_paths.map( path => readFile( path )))
  // fecth all the promises using Promise.all() .
  .then( file_promises => Promise.all( file_promises ))
  .then( data => {
    // do something with the data array that is returned, like extracting the title.
    // create the Epub objects by mapping the data values with their titles
  })
  // error handling.
  .catch( err => console.error( err ));
0 голосов
/ 06 сентября 2018

Ваша проблема с readdir, который является асинхронным, поэтому new Epub, как вы уже выяснили, называется до , это callback параметр.

Переключитесь на readdirSync или переместите const option ... new Epub... в параметре callback readdir, после files.forEach.

...