1

I'm using the excellent answer to this question to convert svg images into inline html.

The function looks through the container or body, finds each .svg image and converts them to inline html.

However, it uses a $.get() call to retrieve the svg files which makes the function asynchronous.

I'd like to convert the function to a promise so that I can wait for it to complete before running other things (like adding or removing classes to the new inline html etc)

My current attempt looks like this:

util.convertSvgImages = function(container) {
    var deferred = Q.defer();
    container = typeof container !== 'undefined' ? container : $("body");

    var getsToComplete = jQuery('img.svg', container).length;   // the total number of $get.() calls to complete within the $.each() loop
    var getsCompleted = 0;                                      // the current number of $get.() calls completed (counted within the $get.() callback)

    jQuery('img.svg', container).each(function(index) {
        var img = jQuery(this);
        var imgID = img.attr('id');
        var imgClass = img.attr('class');
        var imgURL = img.attr('src');
        jQuery.get(imgURL, function(data) {
            getsCompleted += 1;

            var svg = jQuery(data).find('svg');                         // Get the SVG tag, ignore the rest

            if (typeof imgID !== 'undefined') {                         // Add replaced image's ID to the new SVG
                svg = svg.attr('id', imgID);
            }

            if (typeof imgClass !== 'undefined') {
                svg = svg.attr('class', imgClass + ' replaced-svg');    // Add replaced image's classes to the new SVG
            }

            svg = svg.removeAttr('xmlns:a');                            // Remove any invalid XML tags as per http://validator.w3.org

            svg.attr('class', img.attr("data-svg_class") + " svg");     // add class to svg object based on the image data-svg_class value

            $('rect', svg).attr("stroke", "").attr("fill", "");
            $('line', svg).attr("stroke", "").attr("fill", "");
            $('path', svg).attr("stroke", "").attr("fill", "");
            $('polyline', svg).attr("stroke", "").attr("fill", "");
            $('polygon', svg).attr("stroke", "").attr("fill", "");
            $('circle', svg).attr("stroke", "").attr("fill", "");

            img.replaceWith(svg);                                       // Replace image with new SVG

            if (getsCompleted === getsToComplete){
                deferred.resolve('OK');
            }
        }, 'xml');
    });
    return deferred.promise;
};

What is the best way to use promises within asynchronous calls inside an $.each() loop ? Using q.all() ?

Community
  • 1
  • 1
goredwards
  • 2,486
  • 2
  • 30
  • 40
  • Yes, you should use `Q.all`. [Do not use `Q.defer()`](http://stackoverflow.com/q/23803743/1048572), just go for `Q($.get(imgUrl)).then(…)` – Bergi Oct 31 '15 at 11:55

1 Answers1

0

jQuery.get returns a promise, so, you can use that, and jquery.map to make an array of promises that can be used in q.all (or Promise.all for that matter )

util.convertSvgImages = function(container) {
    // ... snip
    var promises = jQuery('img.svg', container).map(function(index) {
        // ... snip
        return jQuery.get(imgURL, function(data) {
            // ... snip
        }, 'xml');
    });
    return q.all(promises);
};

usage

util.convertSvgImages().then(.....)

Note: if any .get fails, the Q.all will reject as soon as there is a failure rather than resolve once all .gets have finished ... you may want to use q.allSettled instead - this will wait for all .get to finish (success or fail) and you can check for failures in your .then code. see documentation for q.allSettled

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87