tinify / tinify-nodejs

Node.js client for the Tinify API.
https://tinypng.com/developers
MIT License
423 stars 74 forks source link

Promise handling or similar behavior #5

Closed uded closed 7 years ago

uded commented 8 years ago

Hi,

It is very possible that I am doing something wrong here, but after giving a try for a while I am empty of any ideas.

When processing an image on AWS Lambda, I am using a regular tinify NodeJS API. My problem is that I am loosing control of the promise while executing the API call to do a remote store to S3. It seems, and this is just my guess based on the behavior of the app, that before the code can be executed in a controllable manner it's actually finished by context.succeed() call. As in the sample below (sizes is a simple array of objects here):

    var url = s3.getSignedUrl('getObject', params);

    var source = tinify.fromUrl(url);
    sizes.forEach(function (size, callback) {
        console.log("Processing size:", size);
        var resized = source.resize({
            method: "fit",
            width: size.width,
            height: size.height
        });

        resized.store({
            service: "s3",
            aws_access_key_id: "XXXX",
            aws_secret_access_key: "XXXX",
            region: "XXXX",
            path: "XXXX/" + name + "_" + size.height + "x" + size.width + "." + ext
        });
    });
   context.succeed("Done");

If I will remove the last line the app will never finish, but images will be handled correctly...

I tried o use async library and to figure out how the bluebird library is being used here, but no ideas till now and time is ticking. What I would like to achieve is a way to control the store() operation in a way that I will be notified about the progress and it's finish. Then, using async or any other technique I will be able to control calling context.succeed() properly...

Any suggestions?

mattijsvandruenen commented 8 years ago

There are various ways of making sure that the succeed() function is called when all images have been uploaded. One is indeed by using the async library. I've made a few small modifications to your code to use async.forEach

var url = s3.getSignedUrl('getObject', params);

var source = tinify.fromUrl(url);
async.forEach(sizes, function (size, callback) {
    console.log("Processing size:", size);
    var resized = source.resize({
        method: "fit",
        width: size.width,
        height: size.height
    });

    resized.store({
        service: "s3",
        aws_access_key_id: "XXXX",
        aws_secret_access_key: "XXXX",
        region: "XXXX",
        path: "XXXX/" + name + "_" + size.height + "x" + size.width + "." + ext
    }).location(function(err, result) {
        console.log("Image upload completed for size:", size);
        callback();
    });
}, function(err) {
    console.log("Finished uploading all sizes");
    context.succeed("Done");
});

The resized.store() call returns a ResultMeta object that we define in our tinify library. This object defines several functions such as location(), width() and height() that support a callback function. In the above example I use the location() fuction with a callback function. This allows us to know when an image size is completely uploaded to S3.

I hope this helps you on your way!