【问题标题】:How to write this with Promises?如何用 Promises 写这个?
【发布时间】:2020-03-11 00:45:56
【问题描述】:
var pdfParser = require('pdf-parser')
var fs = require('fs')
var PDF_PATH = __dirname + '/pdfs'
var results = []
var failed = []

fs.readdir(PDF_PATH, function(err, files){
    if(err){
        return console.log(err)
    }
    for(const file of files){
        let the_ent = {
            'name': '',
            'other data': []
        }

        pdfParser.pdf2json(PDF_PATH + '/' + file, function(error, pdf){
            if(error != null){
                console.log(error)
            }else if(pdf['pages'] == undefined){
                failed.push(file)
                console.log(file +' failed')
            }else{
                //populate 'results' array
            }
                console.log(/*pdf_data*/)
                results.push(/*pdf_data*/)

        })
    }
    console.log(results)
    console.log(failed)
    results = JSON.stringify(results)
    //fs.writeFileSync() write results to json
})

今天早上我不知道我怎么了,我不知道如何异步编写这个;显然,脚本一执行,底部的日志/写入文件就会触发。

我尝试过包装异步函数并等待 readdir / pdf 解析而不是使用回调 - 显然不正确。我只是想解析文件夹中的每个 pdf - 将我想要的内容推送到一些数组,然后在循环完成 zzz 后记录它们。

【问题讨论】:

标签: javascript node.js asynchronous promise callback


【解决方案1】:

包装最小的异步tasks into Promises,然后使用async/await将它们组合起来:

   // the Promise wrapper:
   const parsePdf = file => new Promise((res, rej) => pdfParser.pdf2json(file, (err, r) => err ? rej(err) : res(r));

 (async function () { // start an asynchronous context
   const PDF_PATH = __dirname + '/pdfs'; 
   const results = [], failed = []; // prefer const over let

    // reading files in a promising way is already provided natively:
   const files = await fs.promises.readdir(PDF_PATH);

   for(const file of files){ // this is in series, in parallel would probably be faster
     let the_ent = {
         name: '',
         'other data': [], // avoid whitespaces!
     };

     const pdf = await parsePdf(PDF_PATH + '/' +file);

     if(pdf.pages === undefined) { // prefer equality (===) over comparison (==)
        failed.push(file);
        console.log(file + ' failed');
     } else {
        // populate 'results' array
     }             
  }

  console.log(results, failed);
})();

你可能也可以处理文件in parallel

【讨论】:

  • 我正在尝试这种方法的一半 - 你的方法更干净(大声笑)。非常感谢伙计。
【解决方案2】:

我会承诺异步操作并使用async/await。对于fs 操作,请使用新的fs.promises API。对于其他人,请使用util.promisify() 制作承诺版本。

我创建的 parsePDFs 函数的解析值将是一个 JSON 数组和一个失败文件名数组,因此您可以返回这两条信息:

const util = require('util');
const pdfParser = require('pdf-parser');
// make promisified version of the function
const pdfParser.pdf2jsonP = util.promisify(pdfParser.pdf2json);

const fsp = require('fs').promises;
const path = require('path');
const PDF_PATH = path.join(__dirname, 'pdfs');

async function parsePDFs(dir) {
    const files = await fsp.readdir(dir);
    const results = [];
    const failed = [];

    for (const file of files) {
        let the_ent = {
            'name': '',
            'other data': []
        }

        try {
            let pdf = await pdfParser.pdf2jsonP(path.join(dir, file));
            if (!pdf || pdf.pages === undefined) {
                throw new Error("pdf.pages is empty")
            }
            results.push(pdf);
        } catch(e){
            console.log(e);
            failed.push(file);
        }
    }
    // this will be the resolved value of the returned promise
    return {results, failed};
}

parsePDFs(PDF_PATH).then(data => {
    console.log("failed filenames: " data.failed);
    console.log("json results: ", data.results);
    // do something with data.results and data.failed
}).catch(err => {
    console.log(err);
});

注意:您声明,但从不使用变量the_ent

【讨论】:

  • 出于好奇,fs.readdirSyncawait fs.promises.readdir 之间有实际区别吗?
  • @AndreasPizsa - 是的,巨大的差异。 fs.promises.readdir 仍然是非阻塞和异步的。它只是返回一个promise,然后您可以将其与await 一起使用,这比fs.readdir() 的普通回调更易于编程。 fs.readdirSync() 是同步和阻塞的。在服务器环境中,同步 I/O 调用会破坏服务器的可扩展性,因为它会阻塞 ONE Javascript 线程,因此在等待文件操作完成时无法运行其他任何东西。异步调用允许其他事物在完成时运行。
  • 嘿,谢谢!是的 the_ent 被使用了,但为了清洁起见,我删除了对 pdf 对象的大部分操作。
  • 谢谢@jfriend00。我得到了那部分,我得到了 Promise 部分;就在这个例子中,没有其他事情发生,没有其他同步的非阻塞 I/O,它们实际上是一样的,是吗? (啊,刚刚看到你扩展了你的评论,同样的结论,k,谢谢!)
  • @AndreasPizsa - 在非服务器环境中(就像一个独立的脚本来完成可能是这样的任务),使用同步文件 I/O 是完全可以的,有时它更容易以这种方式编程。只是永远不要在服务器环境或服务器可能想要使用的任何代码中这样做。
【解决方案3】:

您可以使用util.promisify 来承诺同步功能:

const readdir = util.promisify(fs.readdir);
const reader = util.promisify(pdfParser.pdf2json);

最小演示:

const fs = require('fs');
const util = require('util');
var pdfParser = require('pdf-parser');

const readdir = util.promisify(fs.readdir);
const reader = util.promisify(pdfParser.pdf2json);

var PDF_PATH = __dirname + '/pdfs';

(async () => {
    async function processFiles() {
        let results = [];

        let files = await readdir(PDF_PATH);

        for (const file of files) {
            let pdf = await reader(PDF_PATH + '/' + file);
            results.push(pdf);
        }

        return results;
    }

    const result = await processFiles();

    console.info(result);
})();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-21
    • 2012-11-01
    • 2011-07-21
    • 2017-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多