0

I need to resize some images. Problem is the library I'm using do one resize per callback.

I want to resize several images so I put it in a loop:

exports.resizeImages = function (req, res) {
    var images = fs.readdirSync('uploads/');

    for (var n = 0; n < files.length; n++) {
        var tgt = 'uploads/resized/' + images[n];

        gm(tgt).resize(150).write(tgt, function (err) {
            if (err) {
                console.log('resizing failed');
                res.status(400).send('failed to resize');
                return;
            }
            if (n == images.length) {

                res.status(200).send();
            }
        });
    }
}

I'm aware I can't do it like this. I need make the loop wait until the callback responds somehow. I've seen some examples but I can't get it to work.

Any ideas?

3
  • You need to use Promises. Commented Jun 8, 2015 at 15:23
  • where's files coming from? Commented Jun 8, 2015 at 15:25
  • What do you want to do if only one of the files is failing, would you still send 400? Commented Jun 8, 2015 at 15:25

4 Answers 4

1

You could also use the node async module

Something like this:

var async = require('async');

exports.resizeImages = function (req, res) {
    var images = fs.readdirSync('uploads/');

    async.each(images, function(file, callback) {
        var tgt = 'uploads/resized/' + file;
        gm(tgt).resize(150).write(tgt, callback);
    }, function(err) {
        if(err) {
            console.log('resizing failed');
            return res.status(400).send('failed to resize');
        } else {
            //no error
            return res.status(200).send();
        }
    });
}
Sign up to request clarification or add additional context in comments.

Comments

0

You need to write a for loop using promises. Pick your favorite promise library. The details are covered in this question or this question. Or in this blog post:

var Promise = require('bluebird');

var promiseWhile = function(condition, action) {
var resolver = Promise.defer();

var loop = function() {
    if (!condition()) return resolver.resolve();
    return Promise.cast(action())
        .then(loop)
        .catch(resolver.reject);
};

process.nextTick(loop);

return resolver.promise;

};

Comments

0

Promises are great for this, indeed. There are other libraries that wrap callbacks into this kind of abstractions, but since Promises are standard it's better to learn just one thing.

If you want to keep it bare, you can use an external counter:

var images = fs.readdirSync('uploads/');
var processed = 0;

for (var n = 0; n < files.length; n++) {
    var tgt = 'uploads/resized/' + images[n];

    gm(tgt).resize(150).write(tgt, function (err) {
        if (err) {
            console.log('resizing failed');
            res.status(400).send('failed to resize');
            return;
        }
        processed++;
        if (processed == images.length) {
            res.status(200).send();
        }
    });
}

That is assuming that you only send 200 OK if all images have been correctly resized.

Comments

0

Like others have mentioned you could use promises, but if you don't want to start using promises in your project async.js is perfect for this kind of thing.

Your code rewritten using async.js:

exports.resizeImages = function (req, res) {

    async.waterfall([
        function readImages_step(done) {
            readdir('uploads/', done);
        },
        function uploadImages_step(images, done) {
            async.each(images, function(image, cb) {
                var target = 'uploads/resized/' + image;

                gm(target).resize(150).write(target, cb);
            }, done);
        }
    ], function (err) {
        if (err) {
            console.log('resizing failed');
            return res.status(400).send('failed to resize');
        }
        return res.status(200).send();
    }
};

I changed your readdirsync call to be asynchronous. Async.each will run each upload in parallel.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.