2

I have a function to delete a list an array of files, filePaths, by calling a single deleteFile(filePath) on each of the files in a list (some APIS I'm using don't support bulk delete). The function deleteFile return a jQuery promise and resolves/rejects upon deleting the file.

function deleteFiles(filePaths)
    var deferreds = $.map(fileKeys, function (val, index) {
        return deleteFile(filePath);
    });

    $.when.apply($, deferreds).then(function (schemas) {
        console.log("DONE", this, schemas);
        deferred.resolve();
    }, function (error) {
        console.log("My ajax failed");
        deferred.reject(error);
    });

I am getting .reject calls on some of the files in the list (and I know they exist), so I'm thinking I might need to turn the array of filePaths into a chaining of calls, like a queue (b/c this isn't what $.when does, does it? it seems to launch them all at once). I have know idea how to do this (like .deleteFile(path1).deletePath(path2). etc when they are in an array like this.

Any help is appreciated in advance.

Bradley Bossard
  • 2,439
  • 2
  • 23
  • 30
  • even if one of the ajax request is a failure then $.when will call the reject handler... what do you want to do in case one is failed – Arun P Johny Nov 16 '13 at 02:22

2 Answers2

4

$.when doesn't launch anything, they were launched in your map loop. $.when simply returns a promise for an array of promises.

If you want them sequentially, use reduce:

function deleteFiles(filePaths) {
    return filePaths.reduce(function(cur, next) {
         return cur.then(function() {
             return deleteFile(next);
         });
    }, $().promise());
}

If you want them sequentially, while also getting the array back with respective results:

function deleteFiles(filePaths) {
    var ret = filePaths.slice(0);
    return filePaths.reduce(function(cur, next, i) {
         return cur.then(function() {
             return ret[i] = deleteFile(next);
         });
    }, $().promise()).then(function(){
         return $.when.apply($, ret);
    })
    //These don't make any sense to call in this function but sure
    .then(function(schemas) {
         console.log("DONE", this, schemas);
    }).fail(function(error) {
         console.log("My ajax failed");
    });
}
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Awesome, thanks so much for this. I've never encountered the array reduce method and am still reading up on exactly how it works, but your first code snippet seems to work, so I am gracious. – Bradley Bossard Nov 18 '13 at 19:38
1

this isn't what $.when does, does it? it seems to launch them all at once

New updated answer: I have just found out that for new versions of jQuery (>1.8), we can chain jQuery promises simply using $.then. In your case, you could try:

function deleteFiles(filePaths){
    var d = jQuery.Deferred(), 
    for (var i=0;i<filePaths.length;i++){
        d.then(deleteFile(filePath[i]));
    }

    d.resolve();
}

You could take a look at my similar answer at jQuery Deferred and Promise for sequential execution of synchronous and asynchronous funcitons

Old answer:

As I understand, you need to handle the returned value one-by-one, I think that you don't need to care about the orders of executing callbacks as the next deleteFile does not depend on the previous one's result. If that's the case, just try this:

 function deleteFiles(filePaths){
        $.each(filePaths,function(index,val){
              deleteFile(val)
                .done(function(schema){

                })
                .fail(function(error){

                });
        });
   }

Another way to write this if you need to return deferreds:

function deleteFiles(filePaths)
    return $.map(fileKeys, function (val,index) {
        return deleteFile(val);
    });
}

$.each(deleteFiles(yourfilePaths),function(index,val){
    val
    .done(function(schema){
    })
    .fail(function(error){
    });
});

If you do need to chain them, I think you can create a recursive function like this:

function deleteFiles(filePaths, currentIndex){
    if (filePath.length < currentIndex + 1){ //Stop condition.
        return;
    }

    var promise = deleteFile(filePath[currentIndex]);
    if (promise){
        promise
         .done(function(schema){
            //do your job with returned value, decide whether to call the next path in the chain
            deleteFiles(filePaths,currentIndex++);
         })
         .fail(function(error){
              //I assume that you don't call the next path if the current one returns error.
         });
    }
}
Community
  • 1
  • 1
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • Thanks for your advice. I do need to chain them b/c the calls I am making are to the filesystem, and I believe the reason my individual promises started failing is b/c too much simutaneous file access, so I need to do them sequentially. I like the reduce solution better than the recursive function as it's a little bit cleaner though. Thanks for the answer. – Bradley Bossard Nov 18 '13 at 19:41
  • @Bradley Bossard: try the new solution I've just found out by using $.then which makes the code very simple. – Khanh TO Nov 21 '13 at 14:32