0

I have a phonegap application and I want it to upload multiple files to a server which is an async operation. However, because of bandwidth concerns I want to upload the files sequentially and alert the user to the progress.

However, I'm a bit stuck. Since the api is non-blocking and I am (somewhat) attempting to block, I'm not sure exactly how to do this.

I need to do something like this:

files[0].upload().done = function() {
  files[1].upload().done = function() {
    files[2].upload().done = function() {
      files[3].....files[n]
    }
  }
}

How can I do this? At this time, I don't care about failed uploads.

Malfist
  • 31,179
  • 61
  • 182
  • 269
  • I've read that this is probably solved by `promises` but I'm not exactly for sure what they are and how to use them. – Malfist Nov 10 '14 at 19:02
  • What is this `upload()` method, and why does it return an object that gets a `.done` property assigned? – Bergi Nov 10 '14 at 19:20
  • Upload is a wrapper around http://docs.phonegap.com/en/3.0.0/cordova_file_file.md.html#FileTransfer upload function. done is the callback for success. – Malfist Nov 10 '14 at 19:24
  • You really should use a proper [promise](http://stackoverflow.com/a/22562045/1048572) to wrap the function, instead of that thing. – Bergi Nov 10 '14 at 19:33
  • That's kinda the point of this question, I'm not sure how... – Malfist Nov 10 '14 at 19:40
  • 1
    You might want to take a look at [How do I convert an existing callback API to promises?](http://stackoverflow.com/q/22519784/1048572) and [How to sequentially run promises with Q in Javascript?](http://stackoverflow.com/q/18386753/1048572) – Bergi Nov 10 '14 at 20:25

2 Answers2

1

In JavaScript you can define a function that returns a function.

function get_callback(index){  
  return function(){
    // TODO: check if files[index] exists
    files[index].upload().done = get_callback(index+1);
  }
}

files[0].upload().done = get_callback(1);
Heavy
  • 1,861
  • 14
  • 25
1

You could use a Promises/Futures library, such as FuturesJS and one of its components, Sequence.

The sequence module allows one to chain asynchronous functions through callbacks. You first need to create the sequence, then append as many callbacks as you need. Every callback to the sequence object receives at least two arguments, next and err. You need to call next when the asynchronous function ends.

Something like this should work:

var Sequence = require('sequence').Sequence,
    sequence = Sequence.create();

sequence
    .then(function (next, err) {
        if (err) {...}
        files[1].upload(next);
    })
    .then(function (next, err) {
        if (err) {...}
        files[2].upload(next);
    })
    .then(...)
    .then(function (next, err) {
        console.log('all files uploaded');
    });

Your upload function must receive a callback in order to the code above to work. If it doesn't, just change it in the following way:

var upload = function (callback) {
    //the rest of your code
    //at the very end
    callback();
}
  • How can I make the sequence work with in a loop for each file? – Malfist Nov 10 '14 at 20:23
  • For a list of arbitrary files, you could use [Join](https://github.com/FuturesJS/join) instead. Require join with `var join = require('join').Join.create();` then for each file upload, you use `join.add()` as a callback: `for (var i = 0; i < files.length; i++) file[i].upload(join.add())`. Then you register a callback to the join object: `join.then(function () {console.log('all files uploaded');})`. I just realized that this might be a better approach than the one I mentioned before... – Vinicius Teixeira Nov 10 '14 at 21:10