9

I have array (f.e. it is queue of files):

[{deferred: fileDef, data: file}, {...}, ...]

Each fileDef and file send to upload function which return fileDef.promise and call fileDef.resolve or fileDef.reject after uploading.

I want upload files in order: next file upload after previous file is loaded.

Now I use

var queue = [];
var uploading = false;

//file input callback call each time the user selects files
function addAndUpload (file) {

  queue.push({deferred: $q.defer(), data: file});

  if (!uploading) recurceQueue();

  function recurceQueue () {
    if (queue.length) {
      uploading = true;
      var fileObj = queue.shift();
      upload(fileObj.deferred, fileObj.data);

      fileObj.deferred.promise.finally(function () {
        uploading = false;
        recurceQueue();
      })
    }
  }
}

But it seems bad. How to write better?

tamtakoe
  • 245
  • 3
  • 12

1 Answers1

10

Don't use a queue and that boolean flag, just have one variable storing a promise representing all uploads. Also, your upload function should not take a Deferred object to resolve as an argument, but simply return a new promise.

Then the addAnUpload becomes as simple as

var allUploads = $q.when(); // init with resolved promise

function AddAnUpload(file) {
    allUploads = allUploads.then(function() {
        return upload(file);
    });
}

With the closure, you don't need that queue to store the waiting uploads any more. If you want allUploads to fulfill always, even if one upload fails, you need to return an always-fulfilling promise from the then-callback:

        return upload(file).then(null, function(err) {
            console.log(err, "does not matter");
        }); // fulfills with undefined in error case
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • See example: https://github.com/tamtakoe/oi.file/blob/master/oi.file.js#L251 I can't use promise in queue becase I need reject and resolve in transport functions – tamtakoe Oct 17 '13 at 13:31
  • @tamtakoe: I don't see the problem with that? – Bergi Oct 17 '13 at 22:45
  • no problem. I only think that my code unbeautiful because I use array of deffereds instead of array of promises – tamtakoe Oct 31 '13 at 17:50
  • There's nothing wrong with using deferreds (internally), they work for this just like promises. – Bergi Oct 31 '13 at 17:52
  • This is a very elegant solution to dynamically building queues with $q! You can always build a more complex wrapper for every single upload if you wanted to do more complex per-upload handling. – Christian Rondeau Mar 09 '14 at 16:21
  • 1
    Note that $q.defer().resolve() does NOT return a promise (at least not on the current version of AngularJS), so the allUploads object has to be built in three steps; var queueDeferred = $q.defer(); var queue = queueDeferred.promise; queueDeferred.resolve(); – Christian Rondeau Mar 09 '14 at 16:29
  • @ChristianRondeau: It doesn't? Ugh. Maybe more luck with `$q.when(undefined)`. – Bergi Mar 09 '14 at 18:12