如何使用node-pdfkit的异步函数?

时间:2014-12-22 14:58:39

标签: node.js asynchronous node-pdfkit

我正在尝试使用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添加内容,但我不想把所有内容(几百行)放在一个回调中,因为我需要异步函数操纵图像。

有没有办法让这种操作同步?

1 个答案:

答案 0 :(得分:3)

UPDATE_2

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

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

更新

在您的方案中使用承诺:

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

  }
);

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

一种选择是在开始操作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>