Search code examples
javascriptangularjsquickbloxangular-promise

Angular JS promise/defer: using framework Quickblox, How to upload images and then upload public urls to custom object


I am making an app where I'd like users to be able to upload 5 images of themselves.

I am using a backend-as-a-service (Quickblox), which requires me to create the blob files and upload them individually. Upon completion of each upload, I am provided a success callback with the amazon aws url of that image.

I then need to take all the amazon aws urls and upload them back to Quickblox again, to tie the profile with those specific urls. (If you are familiar with Quickblox, I have a custom object that has 5 fields as image urls).

I am unsure of the proper way to do this in angular js.

I'm currently chaining 5 promises to upload each image, and a 6th promise to upload the amazon urls once available. My issue is that the last promise is firing before it receives all the amazon aws urls. Is there a way to do this more properly? I seem to be misunderstanding promises and defers...

Here is my code:

function QBCreateAndUpload(q,scope,i){

// async call to post image
QB.content.createAndUpload({'file':scope.image_blobs[i],'name':"profilepics.jpg", 'type':"image/jpeg", 'public': true },function(e,r){
        if(e){
          return q.reject("error");
        } else {
          scope.amazon_urls[i]= "http://qbprod.s3.amazonaws.com/" + r.uid;
          return scope.amazon_urls;
        }
      });
}

var deferred = $q.defer();
var promise =   
deferred.promise.then(QBCreateAndUpload($q,$scope,0))
.then(QBCreateAndUpload($q,$scope,1))
.then(QBCreateAndUpload($q,$scope,2))
.then(QBCreateAndUpload($q,$scope,3))
.then(QBCreateAndUpload($q,$scope,4))
.then(function(result){
    //async call to upload amazon urls
    var data = {
      _id: principal.data_object_id,
      image1: results[0],
      image2: results[1],
      image3: results[2],
      image4: results[3],
      image5: results[4]
    }


    QB.data.update(className,data,function(e,r){
        if (e){
        } else {
          console.log(r);
          return "all uploaded";
        }
    });

});
deferred.resolve();

Solution

  • Your QBCreateAndUpload function does not resolve/reject the promise in the right way, which means that each of your then functions are unreliable. It appears to me that you don't quite understand how promises work with in general.

    I've tried to keep most of the function as it was and kept it simple in general. Usually you should avoid passing around scopes

    function QBCreateAndUpload(deferred, scope, i) {
      QB.content.createAndUpload({'file':scope.image_blobs[i],'name':"profilepics.jpg", 'type':"image/jpeg", 'public': true }, function(e,r) 
        {
          if (e) 
          {
            deferred.reject(e);
          } 
          else 
          {
            scope.amazon_urls[i] = "http://qbprod.s3.amazonaws.com/" + r.uid;
            deferred.resolve("http://qbprod.s3.amazonaws.com/" + r.uid);
          }
      });
      // the promise is immediately returned
      // the reject() and resolve() functions are called when the async call finishes
      return deferred.promise;
    }
    
    // we will use this array of promises to keep track of our progress
    var promises = [];
    
    for (var i = 0; i < 5; i++) {
      var deferred = $q.defer();
      // we pass the deferred object into the function
      // the function will execute reject() or resolve() 
      // as soon as the async function finsishes
      var promise = QBCreateAndUpload(deferred, scope, i);
      // we store the promise object that the QBCreateAndUpload function
      // immediately returns into our array
      promises.push(promise);
    }
    
    // the all function creates a new promise that will resolve as soon as all
    // promises in our promises array have resolved; if even one fails
    // the all promise fails as well
    $q.all(promises).then(function (results) {
      // the results object contains all objects that were passed when the promises 
      // were resolved; this seems a little pointless since you passed the scope into 
      // the function which would contain the image URLs anyway
      var data = {
        _id: principal.data_object_id,
        image1: results[0],
        image2: results[1],
        image3: results[2],
        image4: results[3],
        image5: results[4]
      }
    
      QB.data.update(className,data,function(e,r){
        if (e)
        {
        } 
        else 
        {
          console.log(r);
          // return "all uploaded";
          // the return is absolutely pointless since you were not synchronous
          // you would need another promise / callback at this point
        }
    }, function (error){
      console.log(error);
    });