调整一个不可链接到 return 值的函数

Adapting a function that isnt chainable to return a value

我正在尝试使用 pdfreader package 在一个对象中获取 pdf 的所有页面。该函数最初 return 在处理每个页面时(作为它自己的对象)。我的目标是编写一个 return 将所有页面作为页面对象数组的包装器。有人可以解释为什么这不起作用吗?

我试过了:

添加 .then 和 return 条件 - 因为我希望 parseFileItems 方法 return 一个值:

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: Cannot read property 'then' of undefined


我正在修改的函数(来自包文档):

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

如果你想链接一个 then,你需要回调函数到 return 一个 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);
    });

这里好像同时有好几个issues/misconceptions。让我们一次看一次。

首先,您似乎认为外部函数将 return ("pass on") 您的回调的 return 值

如您所见,情况并非如此in the library source

此外,它甚至没有意义,因为回调为每个项目调用一次。因此,对于 10 个项目,它将被调用 10 次,那么 parseFileItems 怎么知道你的回调的 10 个 return 值中的哪一个传递给外部?

回调函数中的 return 并不重要,因为 parseFileItems 函数会忽略它。此外,parseFileItems 函数本身也没有 return 任何东西。因此,new pdfreader.parseFileItems(...) 的结果将始终评估为 undefined(而 undefined 显然没有 属性 then)。

其次,您似乎认为 .then 是某种通用的函数调用链接方法。

事实上,.then 是一种链接 承诺 或对承诺的实现做出反应的方法。在这种情况下,任何地方都没有承诺,特别是 parseFileItems 不是 return 承诺(它 return 是 undefined 如上所述),所以你不能调用.then 结果。

根据 the docs,您应该自己对错误和流结束做出反应。因此,您的代码将像这样工作:

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

但是,我同意拥有一个 promise 包装器会更好,这样您就不必将以下所有代码都填充到回调的 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)
})

注意:使用 async generators 你会得到更好的代码,但现在在这里解释太多了,因为从回调到异步生成器的转换没有你想象的那么简单想想。