调整不可链接的函数以返回值

Posted

技术标签:

【中文标题】调整不可链接的函数以返回值【英文标题】:Adapting a function that isnt chainable to return a value 【发布时间】:2020-03-23 15:56:22 【问题描述】:

我正在尝试使用pdfreader package 在一个对象中获取 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: 无法读取未定义的属性“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);
  
);

【问题讨论】:

你似乎在期待parseFileItems() 回报一个承诺。它不返回任何东西,它只是调用回调函数。如果您希望它以这种方式工作,您需要创建自己的 Promise。 【参考方案1】:

这里似乎同时存在几个问题/误解。让我们试着一次看一次。

首先,您似乎认为外部函数将返回(“传递”)您的回调的返回值

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

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

从回调函数返回什么并不重要,因为parseFileItems 函数只是忽略它。此外,parseFileItems 函数本身也不返回任何内容。因此,new pdfreader.parseFileItems(...) 的结果将始终评估为undefined(而undefined 显然没有属性then)。

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

事实上,.then 是一种链接promises 的方式,或者对承诺的履行作出反应。在这种情况下,任何地方都没有任何承诺,特别是parseFileItems 不会返回承诺(它返回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 可以得到更好的代码,但现在在这里解释太多了,因为从回调到异步生成器的转换没有你想象的那么简单。

【讨论】:

您的第一个示例效果很好,但是当我使用承诺版本时,它永远不会返回任何内容 你能描述一下你的意思吗?是否输出done 0 哦,我忘了打电话给parseFileItems,对不起!更新了我的答案【参考方案2】:

如果你想链接一个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);
    );

【讨论】:

从回调中返回不一定会从parseFileItems返回!正如您在library source 中看到的那样,它确实没有。

以上是关于调整不可链接的函数以返回值的主要内容,如果未能解决你的问题,请参考以下文章

Firebase:snapshot.val 不是函数或其返回值不可迭代

20170313 ABAP以jason 格式返回值到http(接口内容返回)

C ++返回指针值不可更改

Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )

Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )

golang os清空返回值