j.kaspar
j.kaspar

Reputation: 761

jquery .done() still runs before deferred.resolve()

I have read several questions here on this topic and I still can't see what is wrong. I have two functions, one initializes an image from a file and reads its dimensions, setting them to object's variables. The second one checks if the dimensions are within limits.

The functions:

  checkDimensions: function() {
      console.log('entering chekDimensions');
      if (options.maxDimensionsWH > 0) {

        console.log(this.checkWidth);
        console.log(this.checkHeight);
        if ((this.checkWidth <= options.maxDimensionsWH || this.checkHeight <= options.maxDimensionsWH)
             && (this.checkWidth > 0 && this.checkHeight > 0)) {
            console.log('returning true');
            return true;    
        } else {
            console.log('returning false');
            return false;
        }

    } else {
        return true;
    }
  },
  initializeCheckImage: function(file) {
    console.log('entering initialization');
    var d = $.Deferred();
    var reader = new FileReader();
      reader.onload = function (e) {
        var img = new Image;
        img.onload = function() {
          this.checkWidth = img.width;
          this.checkHeight = img.height;
          console.log('initializing the image');
          console.log(this.checkWidth);
          console.log(this.checkHeight);
          d.resolve();
        };
        console.log('assigning reader.result');
        img.src = reader.result;
      };
      console.log('assigning a file to the reader');
      reader.readAsDataURL(file);
      console.log('returning deferred');
      return d.promise();
  }

And how they are called:

this.initializeCheckImage(file).done(check = this.checkDimensions());

From the console, it is clearly visible, that execution of the second function occurs before the d.resolve(); is called.

> 21:13:34.460 entering initialization
> 21:13:34.461 assigning a file to the reader
> 21:13:34.462 returning deferred
> 21:13:34.462 entering chekDimensions
> 21:13:34.462 0
> 21:13:34.463 0
> 21:13:34.463 chekDimensions returning false
> 21:13:34.478 assigning reader.result
> 21:13:34.493 initializing the image
> 21:13:34.494 30
> 21:13:34.494 30

What am I doing wrong? Thank you!

Upvotes: 0

Views: 199

Answers (2)

Shahar Shokrani
Shahar Shokrani

Reputation: 8762

The promise function is being immediatly invoked:

this.initializeCheckImage(file).done(check = this.checkDimensions());

The done function should accept as a parameter an executer:

A function that is passed with the arguments resolve and reject. The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object).

So it should be only a reference to function, notice that when you call it with checkDimensions() the JS execute the function immediately.

So you will need it to be wrapped with a function reference, but the problem is the context of function has changed and the checkDimensions() is no longer exist inside the new context.

In order to keep the dimension variable context you can call the checkDimensions function from inside the img.onload with:

if (checkDimensions(img.width, img.height)) {
    return d.resolve();; 
}
else {
    return d.reject("Wrong dimensions");
}

this.initializeCheckImage(file)
    .then(() => console.log("RESOLVED"))
    .catch(() => console.log("REJECTED"));

Edit:

In order to keep the context of the wanted object you can use binding, with bind().

var readerOnload = function(e) {
    //code
}

reader.onload = readerOnload.bind(this);

Or with:

var self = this;

reader.onload = function() {
    //you can use self
}

In your case will have to do it once again for imgOnload.

Upvotes: 1

Alan Lins
Alan Lins

Reputation: 436

I would suggest pass the value to resolve and receive on done.

  checkDimensions: function(dimensions) {
      console.log('entering chekDimensions');
      if (options.maxDimensionsWH > 0) {

        console.log(dimensions.width);
        console.log(dimensions.height);
        if ((dimensions.width <= options.maxDimensionsWH || dimensions.height <= options.maxDimensionsWH)
             && (dimensions.width > 0 && dimensions.height > 0)) {
            console.log('returning true');
            return true;    
        } else {
            console.log('returning false');
            return false;
        }

    } else {
        return true;
    }
  },
  initializeCheckImage: function(file) {
    console.log('entering initialization');
    var d = $.Deferred();
    var reader = new FileReader();
      reader.onload = function (e) {
        var img = new Image;
        img.onload = function() {
          this.checkWidth = img.width;
          this.checkHeight = img.height;
          console.log('initializing the image');
          console.log(this.checkWidth);
          console.log(this.checkHeight);
          d.resolve(
          {
             width:img.width,
             height:img.height
          });
        };
        console.log('assigning reader.result');
        img.src = reader.result;
      };
      console.log('assigning a file to the reader');
      reader.readAsDataURL(file);
      console.log('returning deferred');
      return d.promise();
  }

then

this.initializeCheckImage(file).done(function(dimensions) 
{ 
check = this.checkDimensions(dimensions) 
});

Upvotes: 0

Related Questions