2

I'm running a function on each loop iteration, however I need to ensure that I receive the callback (success) before the next iteration runs. When I run the code below it runs the loops and then the console shows all the callback results after.

var getExerciseImages = function(exerciseImages) {

    var deferred = $q.defer();
    var imageColumns = [];
    var imageObject = {};

    for (var i = 0; i < exerciseImages.length; i++) {

        var url = 'http://files.s3.amazonaws.com/medium/' + exerciseImages[i] + '.jpg'

        convertImgToBase64URL(url).then(function(result) {

            imageObject = {
                image: result,
                fit: [200, 200]
            };

            imageColumns.push(imageObject);
        });

    };

    deferred.resolve(imageColumns);

    return deferred.promise;
};

Calling the outer function:

function convertImgToBase64URL(url, callback, outputFormat) {
    var deferred = $q.defer();

    var img = new Image();
    img.crossOrigin = 'Anonymous';
    img.onload = function() {
        var canvas = document.createElement('CANVAS'),
            ctx = canvas.getContext('2d'),
            dataURL;
        canvas.height = this.height;
        canvas.width = this.width;
        ctx.drawImage(this, 0, 0);
        dataURL = canvas.toDataURL(outputFormat);
        // callback(dataURL);
        deferred.resolve(dataURL);
        canvas = null;
    };
    img.src = url;

    return deferred.promise;
};

I am open to suggestions on other ways to do this if there is a better way?

Taylorsuk
  • 1,419
  • 1
  • 16
  • 51
  • You can't wait for asynchronous results in Javascript. You have to do the next step in the callback function. – Barmar Sep 24 '15 at 19:01
  • Ok, do you have any good resources of how I can three functions in sequence? – Taylorsuk Sep 24 '15 at 19:20
  • Take a look at http://stackoverflow.com/questions/15504921/asynchronous-loop-of-jquery-deferreds-promises. It's for jQuery, but the basic idea is the same for generic promises. – Barmar Sep 24 '15 at 19:23

1 Answers1

1

Asynchronous operations cannot halt execution of a script, however we can refactor your code to avoid the deferred anti-pattern and instead make use of $q#resolve and $q#all in order to be handling all data and logic exclusively with Promises.

var getExerciseImages = function (exerciseImages) {

    // Create a resolved Promise whose value is `exerciseImages` array
    // Use 'when' in place of 'resolve' if using Angular
    return $q.resolve(exerciseImages).then(function (arr) {

        // Map each element to a Promise created by `convertImgToBase64URL`
        // and wait for them all to resolve by wrapping the resulting
        // array of Promises in `$q#all` 
        return $q.all(arr.map(function (url) {

            var url = 'http://files.s3.amazonaws.com/medium/' + url + '.jpg'
            return convertImgToBase64URL(url).then(function (result) {
                return {
                    image: result,
                    fit: [200, 200]
                };
            });
        }));
    });
};

The result of this method would be a Promise whose value is an array of objects each with two properties image and fit:

var images = []; // An array of URLs 
getExerciseImages(images).then(function (data) {
    console.log(data); // => [{image, fit}, ...]
});

As a heads up, in ES7 there are async functions that can be awaited.

sdgluck
  • 24,894
  • 8
  • 75
  • 90
  • Hi, thanks very much for your answer - I'm getting a `$q.resolve is not a function` error. – Taylorsuk Sep 25 '15 at 13:07
  • @Taylorsuk Try `$q.when` [if you are using Angular](http://www.bennadel.com/blog/2735-q-when-is-the-missing-q-resolve-method-in-angularjs.htm). – sdgluck Sep 25 '15 at 13:07
  • Unsure - this is using the Ionic framework. `q.when` sorts the error out. Works well - thanks. – Taylorsuk Sep 25 '15 at 13:26