【问题标题】:How use async functions with node-pdfkit?如何在 node-pdfkit 中使用异步函数?
【发布时间】:2015-02-20 16:44:28
【问题描述】:

我正在尝试使用PDFKit 创建一个 PDF 文件。我插入这样的图像:

var PDFDocument = require('pdfkit');
var doc = new PDFDocument();

doc.image(some_image_as_buffer);

它正在按预期工作。但是现在想要修剪图像,我找到了GraphicsMagick for node.js。但我遇到的问题是让它与 PDFKit 一起工作。 doc.image 需要一个文件名或一个缓冲区,但由于我已经有一个缓冲区,所以我想使用缓冲区(因为缓冲区直接来自数据库,所以任何地方都没有文件)。

修剪工作如下:

var gm = require('gm');
gm(some_image_as_buffer, 'image.png')
  .trim()
  .toBuffer(function(err, trimmed_image_buffer) {
    // trimmed_image_buffer is correct,
    // but I can't put it to the document like this:
    doc.image(trimmed_image_buffer);
    // beacause I don't know which page and/or position
    // the doc is currently on, because of the asynchronous
    // nature of this callback.
  });

更新

澄清:我希望能够在 PDFKit 的同步代码中使用异步修剪图像。 PDFKit 只能同步工作,gm 不提供同步接口。

UPDATE2

var gm = require('gm');
gm(some_image_as_buffer, 'image.png')
  .trim()
  .toBuffer(function(err, trimmed_image_buffer) {
    // trimmed_image_buffer is correct,
    // but I can't put it to the document like this:
    doc.image(trimmed_image_buffer);
    // beacause I don't know which page and/or position
    // the doc is currently on, because of the asynchronous
    // nature of this callback.
  });
doc.text('some text');
// is not guaranteed to run after image is inserted
// and a couple of hundred lines more

在此示例的最后一行之后,还有更多代码行向 PDF 添加内容,但我不想仅仅因为我需要异步函数而将所有内容(几百行)放在一个回调中操作图像。

有什么方法可以使这种操作同步吗?

【问题讨论】:

  • 你的问题到底是什么?您想更好地控制异步流还是想使用与doc.image 不同的东西?如果是后者,那么如果没有进一步调查,我将无能为力,如果是第一个,那么一些基于 Promise 的解决方案应该可以很好地工作。
  • pdfkit.org/docs/guide.pdf(第 5 页)中似乎有一种方法可以使用缓冲页面和缓冲页面范围来获取页码。我不确定您是否已经尝试过?
  • @artur grzesiak:我想要的是,我可以修剪图像并以某种同步方式将其添加到 pdf 中,因为 pdfkit 不能异步工作
  • @harryy000:这不是问题,我知道这是如何工作的,并使用这种技术来呈现页码,但我想要的是,使 gm 的异步代码与同步代码一起工作pdfkit的代码
  • 我希望我不是天真,但是为什么不使用 gm 模块完成所有图像的处理图像裁剪(将它们存储在一个数组中),然后开始与 pdk 相关的处理。

标签: node.js asynchronous node-pdfkit


【解决方案1】:

UPDATE_2

您基本上要求停止执行代码,直到某些异步操作完成。 肯定一般情况下是不可能的。

如果是gm 模块,也不可能。 gm 模块生成一个新进程来执行命令(在您的情况下为 trim()),并且生成新进程的 API 本质上是异步的。

更新

在你的场景中使用 Promise:

var gm = require('gm'),
    Q = require('Q'),
    PDFDocument = require('pdfkit'),
    doc = new PDFDocument();

function getTrimmedImage(some_image_as_buffer){
  var deferred = Q.defer(); 
  gm(some_image_as_buffer, 'image.png')
  .trim()
  .toBuffer(function(err, trimmed_image_buffer) {
    if(err) { deferred.reject(err); }
    else { deferred.resolve(trimmed_image_buffer); }
  });
  return deferred.promise;
}

// here goes all manipulations before the trimmed image is inserted

getTrimmedImage(some_image_as_buffer).then(
  function(trimmed_image_buffer){
     doc.image(trimmed_image_buffer);

     // here goes all manipulations after the trimmed image is inserted

  }
);

正如我在上面的评论中所写,基于 Promise 的解决方案应该可以优雅地工作。我使用Q library,但任何其他承诺库也可以完成这项工作。

一种选择是在开始操作 pdf 之前收集所有异步资源。然后,您可以保证不会发生竞争条件,尽管它可能会减慢整个过程。我使用了一个玩具示例让它在浏览器环境中运行,如果您在将其转换为您的用例时遇到任何问题,请告诉我:

function getAsyncResource(){
  
  var defer = Q.defer();
  
  setTimeout(function(){
    var result = "Some value: " + Date.now();
    console.log("Async resource resolved: " + result); 
    defer.resolve(result);
    }, Math.random() * 5000);
  
  
  return defer.promise;
  }

function someOperationThatNeedsAsyncResources(A, B, C){
  
  console.log("Have all resources: ", A, B, C);
  
  }

var A = getAsyncResource(),
    B = getAsyncResource(),
    C = getAsyncResource();

Q.all([A,B,C]).spread(someOperationThatNeedsAsyncResources);
<script src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.1.2/q.js"></script>

其他选择是将流程拆分为多个步骤,如下所示:

function getAsyncResource(value){
  
  var defer = Q.defer();
  
  setTimeout(function(){
    var result = "Some value: " + value;
    console.log("Async resource resolved: " + result); 
    defer.resolve(result);
    }, Math.random() * 5000);
  
  
  return defer.promise;
  }

function nextStep(resource){
   console.log("Next step: " + resource);
  }

var A = getAsyncResource("A"),
    B = getAsyncResource("B"),
    C = getAsyncResource("C");

A.then(nextStep)
 .then(function(){return B;})
 .then(nextStep)
 .then(function(){return C;})
 .then(nextStep);
<script src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.1.2/q.js"></script>

【讨论】:

  • 对不起,这应该如何与我的示例一起使用,如果我没看错,异步代码仍在异步运行,唯一的区别是,我现在可以在所有异步调用完成后运行一些代码.但我想要 - 因为 PDFKit 同步工作 - 是让代码停止,直到图像的异步修剪完成
  • @jigfox 我更新了我的答案以使其适用于您的场景:首先修剪图像,然后将其插入到 pdf 文档中。如果这对您来说还不够,请提供更多上下文并明确指出错误所在。
  • 图像被修剪和添加是正确的,但在没有承诺和我的原始代码的情况下已经是这样了。我需要完成的是异步代码以某种方式同步,因此我不需要将图像之后发生的完整 PDF 生成放入一个回调中(有关更多详细信息,请参阅我的更新答案)
  • @jigfox 现在我明白了您想要实现的目标——基于回调的解决方案是唯一可行的方法,因为gmImageMagick 作为后台进程运行.
  • 在 ES6 中使用 await 是最好的解决方案
猜你喜欢
  • 2020-04-28
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
  • 2019-04-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-09
  • 1970-01-01
相关资源
最近更新 更多