1

i'm having some issues converting a thumbnail image generation function to a promise.

I need to have it so it runs the Promise.all once the thumbnail has been generated, currently the thumb is undefined (which makes sense as it needs to generate first).

I don't understand the first img.onload part, my work around was to set it on $scope, which i know is a terrible way to pass data around.

    var img = new Image;
    img.onload = resizeImage;
    img.src = $scope.imageData;

    function resizeImage() {
      var newDataUri = imageToDataUri(this, 100, 100);
      $scope.imageDataThumb = newDataUri;
      $scope.$apply();
    }
    function imageToDataUri(img, width, height) {
      // create an off-screen canvas
      var canvas = document.createElement('canvas'),
      ctx = canvas.getContext('2d');
      canvas.width = width;
      canvas.height = height;
      ctx.drawImage(img, 0, 0, width, height);
      var quality = 1.0;
      return canvas.toDataURL('image/jpeg', quality).split(",")[1];  // quality = [0.0, 1.0]
    }

    var imgGuid = factory.guid();

    //Save the image to storage
    console.log($scope.imageDataThumb);
    Promise.all([fireBaseService.postImageToStorage(imageData, "images", imgGuid), fireBaseService.postImageToStorage($scope.imageDataThumb, "images", "thumb_" + imgGuid)])
      .then(function (results) {
      //do stuff with results
      });
Ali_bean
  • 341
  • 3
  • 14
  • Seems like your code is/was clipped off. – Mark Schultheiss Feb 17 '18 at 14:06
  • hi @MarkSchultheiss - it was just the start of the service call to save the thumbnail image – Ali_bean Feb 17 '18 at 14:09
  • Beware, @Bergi is right in [his comment](https://stackoverflow.com/questions/48841966t#comment-84689804): Your problem is caused by a simple asynchronous mis-handling from your part. Actually, it is the same core issue as [this Q/A](https://stackoverflow.com/questions/14220321/). If the accepted answer seems to work for you, it's simply because the image you tried took less than 4ms to load. You will face the same issue with bigger images. The only line you should take from this answer is "*I would recommend moving image loading into the promise as well*". – Kaiido Feb 18 '18 at 04:20

1 Answers1

1

I need to have it so it runs the Promise.all once the thumbnail has been generated,

None of the image exporting functions in canvas will return promises:

toDataURL()

DOMString = canvas.toDataURL(type, encoderOptions); // synchronous

toBlob()

void canvas.toBlob(callback, mimeType, qualityArgument); // asynchronous

The only way is to wrap your function into a Promise manually (although, for a synchronous function such as toDataURL() this makes less sense. If you only generate a thumbnail I would recommend moving image loading into the promise as well as that makes more sense over all since image loading is asynchronous.):

function imageToDataUri(img, width, height) {
  return new Promise(function(success, reject) {
    // create an off-screen canvas
    var ctx = document.createElement('canvas').getContext("2d");
    ctx.canvas.width = width;
    ctx.canvas.height = height;
    ctx.drawImage(img, 0, 0, width, height);
    var quality = 1.0;
    setTimeout(function() {
      success(canvas.toDataURL('image/jpeg', quality).split(",")[1]);  //quality=[0.0,1.0]
    }, 4);  // make call async
  })
}

Now you can use the call as any promise:

var promise = imageToDataUri(img, width, height);
promise.then(function(str) { ... });

or

Promise.all([promise, promise2, ...])
  .then( ... );

Also a small note: by separating the header from the data-uri it's no longer a Data-URL, just a Base-64 encoded string - something to consider for the function name.

  • 2
    `imageToDataUri` is not the problem and should indeed stay synchronous. (If you want, you still can do `Promise.delay(4).then(imageToDataUri)` to get the same effect as the code in your answer). The problem is that OP is calling `resizeImage` as the `onload` handler of his image, but didn't wait for *that*. – Bergi Feb 17 '18 at 15:27