1

I asked this before and tried to change it based on some answers but still have an issue with the promises.

This is actually multiple promises but the main issue is that I am calling pouch.get to get a list of images. I then go through a for/loop to create some markup (which works fine if I don't have the resize promise code). I am trying to create a bunch of thumbnail images to show on a phone in a grid.

I made the resize code a promise to try and get the resize to finish before going and getting another image to resize. But it ends up only doing one onload event for the last image and that is all that shows.

What is happening is that for each loop it goes into the resize, sets the onload event, copies the url to the image then jumps out and does the next loop and the onload event doesn't get fired until the last loop (image), which displays on the screen.

My resize Promise:

function resizeImageToImgPromise(showImage, maxWidth, maxHeight, url) {
   // Set img src to ObjectURL

   return new Promise(function (resolve, reject) {
      var test;
      test = 'test';
      showImage.onload = function () {
     URL.revokeObjectURL(this.src);
     var canvas = document.createElement("canvas");
     var ctx = canvas.getContext("2d");


   ... removed code to make it easier to read and not germane to the issue

     showImage.src = canvas.toDataURL("image/png");
     showImage.width = width;
     showImage.height;
     showImage.style.display = "inline";
     showImage.style.margin = "10px"

     resolve();
      }

      showImage.src = url;
   })
}

Here is the promise that calls this in a for loop:

function readAllImagesFromPouch(id, imageDisplay) {

   return new Promise(function (resolve, reject) {

      var startElement = document.getElementById(imageDisplay);
      var image = "";
      var imgBlob;
      var base64str;

      // Get all attachments for this id

      DB_TaskImages.get(id, { attachments: true }).then(function (doc) {

     for (var key in doc._attachments) {
        var img = document.createElement('img');
        base64str = doc._attachments[key].data;
        blobUtil.base64StringToBlob(base64str).then(function (myBlob) {
           console.log();
           return blobUtil.createObjectURL(myBlob);
        }).then(function (myUrl) {

           img.src = myUrl;

           resizeImageToImgPromise(img, "100", "60", myUrl).then(function () {

          $(startElement).append(img.outerHTML);                              return;
           }).catch(function () {    // this is the catch for the resize
          alert("this is an error");
           })
        }).catch(function (err) {   // this is the catch for the blobUtil
           // error
        });
     }
     return;
      }).then(function () {
     resolve();
      }).catch(function (err) {      // this is the catch for the DB_TaskImages.get
     reject(err);
      })
   });         // end of promise
}

And this is originally called from:

    readAllImagesFromPouch("006", "divImages").then(function () {
    }).catch(function (err) {
       console.log("In catch for readAllImagesFromPouch with err: " + err);
    })
tshad
  • 335
  • 2
  • 4
  • 18
  • I take it my answer previously didn't help at all? http://stackoverflow.com/a/43081942/5053002 - perhaps you could tell me what's wrong with that code - I assume my code didn't work, because your code in this question is still using Promise constructor anti-pattern and blindly looping through `doc._attachments` making asynchronous calls – Jaromanda X Mar 29 '17 at 22:04
  • the main problem with your code here is that `var img = document.createElement('img');` is being assigned in a loop to each `doc._attachments` - the asynchronous code doesn't **start** until the end of the `for...in` loop - therefore, `img` for all the asynchronous code is the last one – Jaromanda X Mar 29 '17 at 22:25
  • also, `this is the catch for the resize` - your `resize` would never reject, because the only source of errors would be inside the `showImage.onload` callback, and these won't become "rejections" - of course, if the `removed` code includes a `reject` call, then you can ignore this comment – Jaromanda X Mar 29 '17 at 22:30
  • @JaromandaX: it was a little confusing. I am not sure what the anti-pattern is. I do know, now, that what you said about the async code not starting until the end of the loop is correct. It actually does all the three lines after the for loop, then it does the "then" of the blobutil the same number of loops and then it does the resize section but doesn't do the onloads until last and then for some reason will do them infinitely. If I comment out the resize promise, it will display all of my images as I wanted (except they won't be resized). I don't really know how to get around this. – tshad Mar 29 '17 at 22:53
  • @JaromandaX: I didn't see the rewrite of my code in the other post. I'll take a look at it and see if it solves my problem. Thanks. – tshad Mar 29 '17 at 23:01
  • it's not a rewrite of your code - it was an answer (I wouldn't rewrite the question :p ) – Jaromanda X Mar 29 '17 at 23:13
  • I hope that in the `... removed code to make it easier to read and not germane to the issue` part, your are nullifying the `showImage.onload` property, otherwise you're in route for a long long loop. – Kaiido Mar 30 '17 at 00:41
  • @Kaiido: I wasn't at first and it was doing exactly as you said. I added a showImage.onload = null just before I added the new image to the .src. Thanks, – tshad Mar 30 '17 at 20:49

1 Answers1

1

Firstly, avoid the promise constructor anti-pattern. As DB_TaskImages.get returns a promise, you don't need to wrap the code in one

Secondly, your for...in loop kicks off a bunch of asynchronous tasks - but you don't actually wait for them to complete

This code will loop through the doc._attachments and execute the resize in "parallel" - only once all resizes have completed will the resized images be shown

function readAllImagesFromPouch(id, imageDisplay) {
    var startElement = document.getElementById(imageDisplay);
    return DB_TaskImages.get(id, {
        attachments: true
    }).then(function(doc) {
        return Promise.all(Object.keys(doc._attachments)
            .map(function(key) {
                var base64str = doc._attachments[key].data;
                return blobUtil.base64StringToBlob(base64str)
                .then(blobUtil.createObjectURL)
                .then(function(myUrl) {
                    return resizeImageToImgPromise("100", "60", myUrl);
                });
            })
        );
    }).then(function(images) {
        images.forEach(function(img) {
            $(startElement).append(img.outerHTML);
        });
    });
}

Note: no error handling done, so any error at any point will result in no images being displayed

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • I added the resize function to see if maybe the promise is causing it to fail. – tshad Mar 30 '17 at 05:24
  • Please don't edit the answer with your code - if you have an answer, you can post an answer, or, edit your question with updated code – Jaromanda X Mar 30 '17 at 05:25
  • I just figured out what was happening here. Inside the onload section, it was moving the changed image into the same image the onload was on. So I guess it just kept running and running. – tshad Mar 30 '17 at 06:06
  • I just needed to null the showImage.src to make it work right. It works correctly now. I just need to look more closely at what you did to understand it. Thanks a lot. – tshad Mar 30 '17 at 06:28