调整不可链接的函数以返回值
我尝试使用 pdfreader 包 在一个对象中获取 pdf 的所有页面。该函数最初在处理每个页面时返回每个页面(作为其自己的对象)。我的目标是编写一个包装器,将所有页面作为页面对象数组返回。有人能解释一下为什么这不起作用吗?
我试过 :
添加 .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: 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);
}
});
这里似乎同时存在几个问题/误解。让我们尝试一次查看它们。
首先,您似乎认为外部函数将返回(“传递”)回调的返回值
事实并非如此,正如您在 库源代码中 看到的那样。
此外,这甚至没有任何意义,因为回调为每个项目调用一次。因此,如果有 10 个项目,它将被调用 10 次,那么
parseFileItems
如何知道要将回调的 10 个返回值中的哪一个传递给外部?
回调函数返回什么并不重要,因为
parseFileItems
函数会忽略它。此外,
parseFileItems
函数本身也不会返回任何内容。因此,
new pdfreader.parseFileItems(...)
的结果将始终计算为
undefined
(并且
undefined
显然没有属性
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)
})
注意:使用 异步生成器 ,您会获得更漂亮的代码,但现在在这里解释太多了,因为从回调到异步生成器的转换并不像您想象的那么简单。
如果你想链接一个
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);
});