Адаптация функции, которая не является цепной, для возврата значения - PullRequest
0 голосов
/ 23 марта 2020

Я пытаюсь получить все страницы PDF в одном объекте с помощью пакета pdfreader . Функция первоначально возвращает каждую страницу (как свой собственный объект) при обработке. Моя цель - написать оболочку, которая возвращает все страницы в виде массива объектов страниц. Может кто-нибудь объяснить, почему это не сработало?

Я пытался :

добавить .then и условие возврата - потому что ожидал, что метод parseFileItems вернет значение:

let pages = [];

new pdfreader.PdfReader()
  .parseFileItems(pp, function(err, item) {
    {
      if (!item) {
        return pages;
      } else if (item.page) {
        pages.push(lines);
        rows = {};
      } else if (item && item.text) {
        // accumulate text items into rows object, per line
        (rows[item.y] = rows[item.y] || []).push(item.text);
      }
    }
  })
  .then(() => {
    console.log("done" + pages.length);
  });

и получил ошибку

TypeError: Невозможно прочитать свойство 'then' из неопределенного


Функция, которую я изменяю (Из документации к пакету):

var pdfreader = require("pdfreader");

var rows = {}; // indexed by y-position

function printRows() {
  Object.keys(rows) // => array of y-positions (type: float)
    .sort((y1, y2) => parseFloat(y1) - parseFloat(y2)) // sort float positions
    .forEach(y => console.log((rows[y] || []).join("")));
}

new pdfreader.PdfReader().parseFileItems("CV_ErhanYasar.pdf", function(
  err,
  item
) {
  if (!item || item.page) {
    // end of file, or page
    printRows();
    console.log("PAGE:", item.page);
    rows = {}; // clear rows for next page
  } else if (item.text) {
    // accumulate text items into rows object, per line
    (rows[item.y] = rows[item.y] || []).push(item.text);
  }
});

Ответы [ 2 ]

1 голос
/ 23 марта 2020

Кажется, здесь есть несколько проблем / заблуждений одновременно. Давайте попробуем взглянуть на них по одному за раз.

Во-первых, вы, кажется, думали, что внешняя функция возвратит ("передаст") возвращаемое значение вашего обратного вызова

Это не case, как вы можете видеть в исходном коде библиотеки .

Кроме того, это даже не имеет смысла, потому что обратный вызов вызывается один раз для каждого элемента. Итак, с 10 элементами он будет вызван 10 раз, и тогда как parseFileItems узнает, какое из 10 возвращаемых значений вашего обратного вызова передается наружу?

Не имеет значения, что вы возвращаете из функции обратного вызова, так как функция parseFileItems просто игнорирует ее. Более того, сама функция parseFileItems тоже ничего не возвращает. Таким образом, результат new pdfreader.parseFileItems(...) всегда будет иметь значение undefinedundefined, очевидно, не имеет свойства then).

Во-вторых, вы, похоже, подумали, что .then - это своего рода универсальный метод связывания для вызовов функций.

На самом деле, .then - это способ связать обещания , или отреагировать на выполнение обещания. В этом случае нигде нет обещаний, и, в частности, parseFileItems не возвращает обещание (оно возвращает undefined, как описано выше), поэтому вы не можете вызвать .then для его результата.

Согласно документам , вы должны реагировать на ошибки и конец потока самостоятельно. Итак, ваш код будет работать так:

let pages = [];

new pdfreader.PdfReader()
  .parseFileItems(pp, function(err, item) {
    {
      if (!item) {
        // ****** Here we are done! ******
        console.log("done" + pages.length) // The code that was in the `then` goes here instead
      } else if (item.page) {
        pages.push(lines);
        rows = {};
      } else if (item && item.text) {
        // accumulate text items into rows object, per line
        (rows[item.y] = rows[item.y] || []).push(item.text);
      }
    }
  })

Однако я согласен, что было бы лучше иметь упаковщик обещаний, чтобы вам не пришлось вставлять весь следующий код в колбэк if (!item) филиал. Вы можете добиться этого, используя new Promise:

const promisifiedParseFileItems = (pp, itemHandler) => new Promise((resolve, reject) => {
  new pdfreader.PdfReader().parseFileItems(pp, (err, item) => {
    if (err) {
      reject(err)
    } else if (!item) {
      resolve()
    } else {
      itemHandler(item)
    }
  })
})

let pages = []

promisifiedParseFileItems(pp, item => {
  if (item.page) {
    pages.push(lines)
    rows = {}
  } else if (item && item.text) {
    // accumulate text items into rows object, per line
    (rows[item.y] = rows[item.y] || []).push(item.text)
  }
}).then(() => {
  console.log("done", pages.length)
}, e => {
  console.error("error", e)
})

Примечание: вы получите еще более приятный код с асин c генераторами но это слишком много, чтобы объяснить здесь сейчас, потому что преобразование из обратного вызова в асинхронный генератор c менее тривиально, чем вы думаете.

0 голосов
/ 23 марта 2020

Если вы хотите связать then, вам нужна функция обратного вызова для возврата Promise:

new pdfreader.PdfReader()
    .parseFileItems(pp, function (err, item) {
        return new Promise( (resolve, reject) => {
            let pages = ...
            // do stuff
            resolve(pages);
        }
    })
    .then( pages => {
        console.log("done" + pages.length);
    });
...