0

I have the following script that I want to use for uploading files. It's working fine, however I'm having trouble trying to find out why every time I upload a new file the previous file that was dragged is also present. Due to the asynchronous nature of the program I'm having trouble debugging this.

Fiddle:

http://jsfiddle.net/tekky/PYTBw/

Code:

var dndbox = $(".dndbox")[0];

dndbox.addEventListener("dragenter", function(e) {
    e.stopPropagation();
    e.preventDefault();
},false);

dndbox.addEventListener("dragover", function(e) {
    e.stopPropagation();
    e.preventDefault();
},false);

dndbox.addEventListener("drop", function(e) {
    e.stopPropagation();
    e.preventDefault();

    var itemList = e.dataTransfer.items;

    var fp = [];

    var traverse = function(entry)
        {
            if (entry.isFile) {
                entry.file(function(file) {
                    var filepromise = new Promise(function(res, rej) {
                        res(file);
                    });
                    fp.push(filepromise);
                });
            } else if (entry.isDirectory){
                var dR = entry.createReader();
                dR.readEntries(function(entries) {
                    for(var i = 0; i < entries.length; i++)
                    {
                        traverse(entries[i]);
                    }
                });
            }
        }

        var collection = [];
        for(var i = 0; i < itemList.length; i++) {
            var e = itemList[i].webkitGetAsEntry();
            var tp = new Promise(function(r, rj) {
                traverse(e);
                r(fp);
            });
            collection.push(tp);
        }

        Promise.all(collection).then(function(ff) {
            $(".prompt").show();
            $(".prompt").on("click", function() {
                $(this).hide();
                ff.forEach(function(pa) {
                    pa.forEach(function(p){
                        p.then(function(f)
                        {
                            console.log(f);
                        });

                    });
                });
            });
        });
},false);
Tek
  • 2,888
  • 5
  • 45
  • 73

1 Answers1

1

every time I upload a new file the previous file that was dragged is also present.

This is not due to the asynchronous nature of traverse and has not much to do with the promises. It's due to this structure:

// left away some irrelevant callbacks
dndbox.addEventListener("drop", function(e) {
    var fp = [];
    // add entries to fp
    $(".prompt").on("click", function() {
        console.log(fp);
    });
}, false);

As you can see, every time something is dropped you create a new list and add a listener to the (same) element that on every click from now on will log that list. To solve that problem, you can:

  • use only one global list that will be overwritten in case of a new drop event
  • create a new .prompt element for each list to log
  • uninstall the listener when it has fired so that it will no be triggered again. You can use the jQuery .one() method for that.

Apart from that, you are using Promises very oddly (not to say, wrong). collection is a list of promises that are immediately resolved with the still empty fp array. You only are lucky that all the files are read in when the click callback is triggered. Still, each pa you're getting === fp, so that you are logging each item in fp as many times as itemList was long.

Have a look at Understanding promises in node.js for recursive function and Is there an example of using raw Q promise library with node to recursively traverse a directory asynchronously? for how to do it properly. In your case, it would be like

function traverse(entries)
    var collection = [];
    for (var i=0; i<entries.length; i++) {
        var entry = entries[i];
        if (entry.isFile) {
            collection.push(new Promise(function(res, rej) {
                entry.file(res);
            }));
        } else if (entry.isDirectory){
            collection.push(new Promise(function(res, rej) {
                var dR = entry.createReader();
                dR.readEntries(res);
            }).then(traverse));
        }
    }
    return Promise.all(collection).then(function(results) { // flatten
        return [].concat.apply([], results);
    });
}

var entries = [];
for(var i = 0; i < itemList.length; i++)
    entries[i] = itemList[i].webkitGetAsEntry();

traverse(entries).then(function(fp) {
    $(".prompt").show().one("click", function() {
        $(this).hide();
        fp.forEach(console.log, console);
    });
});
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Wow... thank you. That looks like one big headache. I understand it but I feel like it would have taken me a long time to figure it out. I guess I just need to get used to using promises. This is the first time I've needed so many of them. I wish I could tip you a drink with some Bitcoin/Dogecoin or something! Again, thank you very much! – Tek Mar 15 '14 at 16:24