仅在完成异步递归函数后延迟函数执行。

时间:2013-11-21 18:40:47

标签: javascript recursion promise

我创建了一个带有拖放的文件上传表单,它在已删除的文件夹上递归迭代并上传文件和文件夹,并将它们添加到JSTree实例。

这是drop call

function drop(evt){
    evt.stopPropagation();
    evt.preventDefault();


    //get selected node (created by jsTree)
    var parent_id = $('#treeContainer').jstree('get_selected').attr('data-attr-itemid');
    var theNodeId = $('#treeContainer').jstree('get_selected').attr('id');

    for (var i=0; i<items.length; i++) {
        var item = items[i].webkitGetAsEntry();
        if (item) {
            traverseFileTree2(item,parent_id,theNodeId);
        }
    }
   //THIS RUNS BEFORE RECUSIVE FUNCTION IS COMPLETED
   addFilesToUploadQueue(filesToQueue);
}

这里是递归遍历函数

var filesToQueue = [];
function traverseFileTree2(item, parent_id, theNodeId, path) {
    if (item.isFile) {
        item.file(function(file){
          //add parent id, and adds to array to be queued for upload
          file.parent_id = parent_id;
          filesToQueue.push(file);
        })
       return;
    }
    else if (item.isDirectory) {

       var dirName = item.name;
       var url = '<?php echo $this->Html->url(array('controller'=>'nodes', 'action'=>'addchild'));?>';
       var data = {'parent_id':parent_id, 'name':dirName, 'type':'Directory'};

       $.post(url,data,function(result){

            //adds directory node to tree
            $("#treeContainer").jstree('open_node','#'+theNodeId);
            $("#treeContainer").jstree('create', '#'+theNodeId, 'last', result,null,true);

            parent_id = result["attr"]["data-attr-itemid"];
            theNodeId = result["attr"]["id"];
            var dirReader = item.createReader();

            dirReader.readEntries(function(entries) {
                for (var i=0; i<entries.length; i++) {
                    traverseFileTree2(entries[i], parent_id, theNodeId, path + item.name + "/");
                }
            });
    }, 'json');
}

}

我的问题是函数addFilesToUploadQueue(filesToQueue)在递归异步函数traverseFileTree2完成之前运行。如果可以使用promises完成,我宁愿不使用回调。

1 个答案:

答案 0 :(得分:2)

这绝对是你可以用promises攻击的问题。

  1. 设计traverseFileTree2()以便始终返回promise / deferred,即使您实际上没有异步启动任何内容。
    • 该承诺的成功处理程序应该提供它找到的文件列表。
    • 如果您发起一系列递归调用,请列出他们的承诺,并使用$.when()等待他们全部完成,然后再与他们合作。
  2. 同样,在您的主循环中,收集顶级承诺列表(来自traverseFileTree2())并再次使用$.when()将它们组合成一个承诺,当所有承诺都“完成”时结束了,汇总了他们的文件列表。 (没有共享filesToQueue变量,它是所有返回值。)
  3. 设置addFilesToUploadQueue()以便在这个巨大的最后承诺成功时调用,将承诺的文件返回数据转移到队列

  4. 这是一个 UNTESTED 重写,但即使它的错误也足以让我们了解这个想法:

    function drop(evt){
        evt.stopPropagation();
        evt.preventDefault();
    
    
        //get selected node (created by jsTree)
        var parent_id = $('#treeContainer').jstree('get_selected').attr('data-attr-itemid');
        var theNodeId = $('#treeContainer').jstree('get_selected').attr('id');
        var promises = [];
        for (var i=0; i<items.length; i++) {
            var item = items[i].webkitGetAsEntry();
            if (item) {
                promises.push(traverseFileTree2(item,parent_id,theNodeId));
            }
        }
        $.when(promises).then(function(filesToQueue){
            addFilesToUploadQueue(filesToQueue);         
        });   
    }
    

    function traverseFileTree2(item, parent_id, theNodeId, path) {
        var ret = $.Deferred();
    
        if (item.isFile) {
            var files = [];
            // What is .file()?? I'm assuming it's synchronous right now           
            item.file(function(file){
              //add parent id, and adds to array to be queued for upload
              file.parent_id = parent_id;
              files.push(file);
            });            
            ret.resolve(files)        
    
        } else if (item.isDirectory) {
    
           var dirName = item.name;
           var url = '<?php echo $this->Html->url(array('controller'=>'nodes', 'action'=>'addchild'));?>';
           var data = {'parent_id':parent_id, 'name':dirName, 'type':'Directory'};
    
           $.post(url,data,function(result){
    
                var promises = [];
                //adds directory node to tree
                $("#treeContainer").jstree('open_node','#'+theNodeId);
                $("#treeContainer").jstree('create', '#'+theNodeId, 'last', result,null,true);
    
                parent_id = result["attr"]["data-attr-itemid"];
                theNodeId = result["attr"]["id"];
                var dirReader = item.createReader();
                // Assuming readEntries() is syncrhonous
                dirReader.readEntries(function(entries) {
                    for (var i=0; i<entries.length; i++) {
                        promises.push(traverseFileTree2(entries[i], parent_id, theNodeId, path + item.name + "/"));
                    }
                });
    
                $.when(promises).then(function(){
                    // IIRC each argument will be the return value of one of the promises
                    var files = [];
                    for(var i = 0; i < arguments.length; i++){
                        files.concat(arguments[i]);
                    }
                    ret.resolve(files);
                },function(){
                    ret.fail();
                });                
           }, 'json');
        }
        return ret.promise(); // .promise() is "safer" in terms of keeping code isolated
    }
    

    请注意,此示例并未过多关注失败案例,其中一个递归目录列表恰好失败。