Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何处理使用承诺递归读取目录树_Javascript_Node.js_Promise_Q - Fatal编程技术网

Javascript 如何处理使用承诺递归读取目录树

Javascript 如何处理使用承诺递归读取目录树,javascript,node.js,promise,q,Javascript,Node.js,Promise,Q,我正在尝试编写一个函数,该函数与使用带承诺模式的回调模式编写的以下函数相同: function readdirRecursive(path,handler,callback) { var errs = [], tree = {}; fs.readdir(path,function(err,dir) { if(err)return callback(err); var pending = dir.length; if(!pending)return

我正在尝试编写一个函数,该函数与使用带承诺模式的回调模式编写的以下函数相同:

function readdirRecursive(path,handler,callback)  {
  var errs = [],
      tree = {};
  fs.readdir(path,function(err,dir)  {
    if(err)return callback(err);
    var pending = dir.length;
    if(!pending)return callback(null,tree);
    dir.forEach(function(file)  {
      var newPath = Path.join(path,file);
      fs.stat(newPath,function(err,stats)  {
        if(stats.isDirectory())  {
          readdirRecursive(newPath,handler,function(err,subtree)  {
            tree[file] = subtree
            handler(tree,newPath,file,"directory",function(err)  {
              if(err)errs.push(err);
              if(!--pending)return callback(errs.length>0?errs:null,tree);
            });
          });
        } else  {
          tree[file] = null; 
          handler(tree,newPath,file,"file",function(err)  {
            if(err)errs.push(err);
            if(!--pending)return callback(errs.length>0?errs:null,tree);
          });
        }
      });
    });
  });
};
这是我目前的尝试:

function readdirRecursive(path)  {
  var tree = {};
  return Q.Promise(function(resolve,reject,notify)  {
    return readdir(path)
    .then(function(dir)  {
      var futures = [];
      var pending = dir.length;
      if(!pending)return resolve(tree);
      dir.forEach(function(file)  {

        var deferred = Q.defer();
        var subPath = Path.join(path,file);
        futures.push(stat(subPath)
        .then(function(stats)  {
          if(stats.isDirectory())  {
            tree[file] = tree;
            var sub = readdirRecursive(subPath)
            sub
            .then(function(subtree)  {
              notify({
                path:subPath,
                name:file,
                type:"directory",
                done:deferred,
                pending:pending
              });
              //return subtree;
            },reject,notify);
          } else  {
            tree[file] = null;
            notify({
              tree:tree,
              path:subPath,
              name:file,
              type:"file",
              done:deferred,
              pending:pending
            });
            //return null;
          }
          //console.log("tree",tree);
          deferred.promise()
          .then(function()  {
            console.log("pending promise");
            if(!--pending)resolve(tree);
          }
          ,function(err)  {
            reject();
          });
        }));
      });
      return Q.all(futures)
      .then(function(futures)  {
        console.log("hi",futures);
      });
    });
  });
};
此代码将在整个树上迭代,但不会返回树,并且会发生通知操作,但延迟承诺永远无法解析

当延迟承诺在notify事件之前启动时,根本不会发生任何事情

我知道我可以通过将一个done函数交给progress事件来解决这个问题,而不是试图给出某种承诺,但我希望在这里尽可能充分地使用承诺,例如,这段代码正是我希望它做的:

function readdirRecursive(path)  {
  var tree = {};
  return Q.Promise(function(resolve,reject,notify)  {
    return readdir(path)
    .then(function(dir)  {
      var futures = [];
      var pending = dir.length;
      if(!pending)return resolve(tree);
      dir.forEach(function(file)  {

        var deferred = Q.defer();
        var subPath = Path.join(path,file);
        console.log("file",file);
        /*deferred.promise()
        .then(function()  {
          console.log("pending promise");
          if(!--pending)resolve(tree);
        }
        ,function(err)  {
          reject();
        });*/
        futures.push(stat(subPath)
        .then(function(stats)  {
          if(stats.isDirectory())  {
            var sub = readdirRecursive(subPath)
            sub
            .then(function(subtree)  {
              tree[file] = subtree
              notify({
                path:subPath,
                name:file,
                type:"directory",
                done:function(err)  {
                  console.log("pending promise");
                  if(err)return reject(err);
                  if(!--pending)resolve(tree);
                },
                pending:pending
              });
              //return subtree;
            },reject,notify);
          } else  {
            tree[file] = null;
            notify({
              tree:tree,
              path:subPath,
              name:file,
              type:"file",
              done:function(err)  {
                console.log("pending promise");
                if(err)return reject();
                if(!--pending)resolve(tree);
              },
              pending:pending
            });
            //return null;
          }
          //console.log("tree",tree);
        }));
      });
      return Q.all(futures)
      .then(function(futures)  {
        console.log("hi",futures);
      });
    });
  });
};
这是将执行这些函数的代码:

readdirRecursive("../").then(function(tree)  {
  console.log("TREE!!!",tree);
},function(err)  {
  console.log("ERROR",err);
},function(progress)  {
  console.log("PRGRESS WAS MADE",progress);
  progress.done();
});

我的第一个想法是简单地将您的原始函数封装在一个承诺中。这通常是我在不重新设计底层代码的情况下执行此操作的方式:

function readdirRecursiveWithPromise (path, handler) {
    return new Promise((resolve, reject) => {
        readdirRecursive(path, handler, (err, tree) => {
            if (err) {
                reject(err);
            }
            else {
                resolve(tree);
            }
        });
    })
}
不幸的是,当我尝试测试这段代码时,我发现您的代码存在一些潜在的问题

首先,我不知道你的“处理者”应该做什么。您没有对此进行解释,也没有描述它应该做什么。这对问题很重要,因为它控制最终回调是否被调用,所以我可以推测“处理程序”控制着这个操作,如果你的“回调”没有被调用,可能是因为“处理程序”中的逻辑

下一个问题是,您的'pending'变量设置为文件和目录的总数,但它仅对目录递减。因此,您的“挂起”变量永远不会达到0,调用回调的条件代码也永远不会被调用

因此,我将去掉“handler”和“pending”,我将向您展示我如何从头开始用承诺重写这篇文章

下面是完整的工作代码示例:。请继续阅读以获得解释

让我们从基于承诺的readdir版本开始,该版本不是递归的:

function readdir (path) { // Promise-based version of readdir.
    return new Promise((resolve, reject) => { // Wrap the underlying operation in a promise.
        fs.readdir(path, (err, files) => {
            if (err) {
                reject(err); // On error, reject the promise.
            }
            else {
                resolve(files); // On success, resolve the promise.
            }
        });    
    });
};
我们还需要一个基于承诺的函数,用于确定特定路径的类型(文件或目录):

function determineType (parentPath, childPath) { // Promise-based function to determine if the path is a file or directory.
    return new Promise((resolve, reject) => {
        fs.stat(path.join(parentPath, childPath), (err, stats) => {
            if (err) {
                reject(err);
            }
            else {
                resolve({ 
                    path: childPath,
                    type: stats.isDirectory() ? 'directory' : 'file' // Check if it's a directory or a file.
                }); 
            }
        });
    });
};
现在我们可以展开
determineType
并创建一个函数,该函数采用一个路径数组并确定每个路径的类型。这使用
Promise.all
并行执行多个异步操作:

function determineTypes (parentPath, paths) { // Async function to determine if child paths are directories or files.

    return Promise.all(
            paths.map(
                childPath => determineType(parentPath, childPath) // Is the path a directory or a file?
            )
        );
};
现在,我们可以构建基于承诺的重新发布版
readdir

function readdirTree (rootPath) { // Read an entire directory tree, the promise-based recursive version.
    return readdir(rootPath) // Initial non-recursive directory read.
        .then(childPaths => determineTypes(rootPath, childPaths)) // Figure out the type of child paths.
        .then(children => {
            return Promise.all(children // Use Promise.all to figure out all sub-trees in a parallel.
                .filter(child => child.type === 'directory') // Filter so we only directories are remaining.
                .map(child => {
                    return readdirTree(path.join(rootPath, child.path)) // It's a directory, recurse to the next level down.
                        .then(subTree => {
                            return {
                                path: child.path,
                                subTree: subTree,
                            };
                        });
                })
            );
        })
        .then(children => {
            const tree = {}; // Reorganise the list of directories into a tree.
            children.forEach(directory => {
                tree[directory.path] = directory.subTree;
            });
            return tree;
        });
};
下面是一个使用示例:

readdirTree("c:\\some-directory")
    .then(tree => {
        console.log("tree:");
        console.log(tree);
    })
    .catch(err => {
        console.error("error:");
        console.error(err);
    });
我在Github中为您提供了一个完整的工作示例:


希望它能帮助您向前迈进。

在bluebird wiki中有一个这样做的例子。我很好地阅读了目录,如果我想将它们读入一棵树,我可以这样做,但我想将它们读入一棵树,然后使用notify事件对它们中的每一个进行一些额外的处理。找到你的例子:我还必须使用Q,因此不推荐使用tag.progress。如果用不同的方式处理它会是最佳选择,尽管我对node/js非常熟练,但我对使用Promissions还是完全陌生。我已经用一些“最终目标”的工作示例更新了代码,删除了一个承诺,但是考虑到这一点,我真的不知道现在该怎么做,因为notify操作是我在这里追求的功能类型。作为堆栈溢出中最响亮的promise倡导者之一-我在这里不会使用它们-你想要流式处理多个结果,而不是一个-如果你想要流式处理它们,我会使用Observable;如果你想要使用promises,我会将它们推到数组中,然后
。所有
阵列。