许多排队操作的Q模式

时间:2014-03-21 15:26:40

标签: javascript node.js promise q

我正在为node.js编写一个小脚本,从文件中读取一堆图像名称(2.5k),调整图像大小并将它们输出到目录。我天真的方式导致文件句柄耗尽:

//get the list of images, one per line in the file
var imgs = file.split('\n');
//keep track of how many images we've processed
var done = imgs.length;
var deferred = Q.defer();

for (var i = 0; i < imgs.length; i++) {
  (function resizeImg(img) {
    //open the file for writing the resized image to
    var stream = fs.createWriteStream('images/' + img);

    stream
      .on('open', function () {
        //now that it's opened, resize the source image, and write it
        //out to the stream
        gm(img)
          .resize(200, 200)
          .write(stream, function (err) {
            //we're finished writing - if there was an error, reject
            //otherwise, we can resolve the promise if this was the last image
            if (err)
              deferred.reject(err);
            else if (--done <= 0)
              deferred.resolve();
          });
      });
  })(imgs[i]);
}

return deferred.promise;

我真正需要做的是排队所有调整大小操作并按顺序运行它们,以便它不会同时打开所有文件,但我不知道如何做到这一点。这种事情有标准模式吗?

2 个答案:

答案 0 :(得分:1)

你能做这样的事吗:

//get the list of images, one per line in the file
var imgs = file.split('\n');
//keep track of how many images we've processed
var done = imgs.length;

//Store an array of functions to be executed in sequence
var funcArr = [];

for (var i = 0; i < imgs.length; i++) {
  //push a promise function onto the array
  funcArr.push((function resizeImg(img) {
    return function () {
        var deferred = Q.defer();

        //open the file for writing the resized image to
        var stream = fs.createWriteStream('images/' + img);

        stream
          .on('open', function () {
            //now that it's opened, resize the source image, and write it
            //out to the stream
            gm(img)
              .resize(200, 200)
              .write(stream, function (err) {
                //we're finished writing - if there was an error, reject
                //otherwise, we can resolve the promise if this was the last image
                if (err)
                  deferred.reject(err);
                else
                  deferred.resolve();
              });
          });

          return deferred.promise;
    }
  })(imgs[i]));
}

//Sequences as described at http://documentup.com/kriskowal/q/
var result = Q();
funcArr.forEach(function (f) {
    result = result.then(f, function (reason) {
        //Default error handler for each image in the sequence that does a reject
    });
});

//At this point result is a promise that will be resolved when all images have processed
return result;

for循环的每次迭代都会将一个返回promise的函数推送到funcArr数组中。在for循环之后使用Q将promises链接在一起进行排序。这应确保在移动到下一个图像之前处理一个图像。

答案 1 :(得分:0)

将调整大小过程分离为返回承诺的函数更容易,但不是必需的。

这使您可以在主程序中看到树木。

function resizeImages(file) {

    //An inner utility function that returns a function that does the hard work and, importantly, that returns promise.
    function resize(img) {
        return function() {
            var deferred = Q.defer(),
                stream = fs.createWriteStream('images/' + img);
            stream.on('open', function() {
                gm(img).resize(200, 200).write(stream, deferred.resolve);//Always resolve, even if error is reported, thus allowing the .then chain to continue.
            });
            return deferred.promise;
        }
    }

    var p = Q();//resolved starter promise

    //main routine - build a .then chain
    for(var imgs=file.split("\n"), i=0; i<imgs.length; i++) {
        p = p.then(resize(imgs[i])).then(function(err) {
            //Yup, we're handling reported errors in the success handler!
            if(err) {
                //Handle error here.
                //throw(something) to stop the process or don't throw(anything) to continue.
            }
        });
    };
    return p;
}