在 express get() 中正确调用下一个回调

Posted

技术标签:

【中文标题】在 express get() 中正确调用下一个回调【英文标题】:Proper calling of next callback in express get() 【发布时间】:2015-06-09 15:28:58 【问题描述】:

我有一个在 nodejs 上运行的快速服务器。我有一个文件结构,我想将其转换为链接列表。我已经包含了我的递归文件结构函数。

文件结构函数

function fileTreeWrapper (root, callback) 
	output = recursiveFileTree(root);
	console.log(output);
	callback();


function recursiveFileTree (root) 
	fs.readdir('./' + root, function (err, files) 
		if(err) throw err;
		var structure = '';
		var folders = '';
		var tests = '';

		structure += '<ul>';
		
		for(var i in files) 
			if(files[i].indexOf('.') === -1) 
				folders += '<li id="folder">' + root + '/' + files[i] + '">' + files[i].charAt(0).toUpperCase() + files[i].slice(1);
				folders += recursiveFileTree(root + '/' + files[i]);
				folders += '</li>';
			 else if (files[i].indexOf('.js') > -1) 
				tests += '<li id="file"><a href="' + root + '/' + files[i] + '">' + files[i] + '</a></li>';
			
		
		structure += folders;
		structure += tests;
		structure += '</ul>';
		return structure;
	);

我这样调用 router.get:

router.get('/', [function (req, res, next) 
    fileTreeWrapper('rootFolder', function() 
        next();
    );
, function (req, res) 
    res.render('Files & Folders', 
        title: 'Main',
        localTitle: 'Choose a File',
        result: output
    , function (err, html) 
        if(err) throw err;
        res.send(html);
        res.end();
    );
]);

但是,当我运行此程序时,我的页面显示为空白。我的jade模板渲染是这样的:

h1= title
h3= localTitle
div !result

我是否不恰当地使用了 next() 回调?为什么这不起作用?

编辑:如果您要否决我的问题,请至少提供评论并说明原因。我是 SO 新手,这样做让我很生气。

【问题讨论】:

看起来你执行callback 太快了 另外,output 在您使用 console.log 时会返回您期望的结果吗?我希望它是未定义的。 是的,输出未定义。 @KevinB 当我这样称呼它时: router.get('/', [function (req, res, next) recursiveFileTree('rootFolder'); next(); );它仍然做同样的事情。 您的递归函数几乎需要完全重写。你需要正确实现异步逻辑,fs.readdir是异步的。 【参考方案1】:

你最初做的事情有点不对劲。我重写了您的所有代码以使其更有意义。希望这会有所帮助。

function recursiveFileTree(root, callback) 
  fs.readdir('./' + root, function (err, files) 
    if (err)  return callback(err); 
    try 
      var structure = '';
      var folders = '';
      var tests = '';
      structure += '<ul>';
      files.forEach(function (file) 
        if (file.indxOf('.') === -1) 
          folders += '<li id="folder">' + root + files[i] + '">' + file.charAt(0).toUpperCase() + file.slice(1);
          folders += recursiveFileTree(root + '/' + file);
          folders += '</li>';
         else 
          tests += '<li id="file"><a href="' + root + '/' + file + '">' + file + '</a></li>';
        
      );
      structure += folders;
      structure += tests;
      structure += '</ul>';
      callback(null, structure);
     catch (err) 
      return callback(err);
    
  )
;


router.get('/', function (req, res) 
  recursiveFileTree('rootFolder', function (err, structure) 
    if (err)  throw err; 
    res.render('fileListingView', 
      title: 'Main',
      localTitle: 'choose a File',
      result: structure
    );
  );
);

注意事项:

看起来您甚至不需要使用中间件函数(将next 作为参数的函数)。将中间件想象成管道。 req 按顺序遍历每个中间件(管道的一部分),直到它到达您的最终处理程序(最终调用 res.sendres.jsonres.render 的处理程序)。

调用res.render 即可将视图发送给客户端。

res.render 的第一个参数是它应该呈现的视图模板的名称。 Files &amp; Folders 在那里传递并没有多大意义。您需要一个名为该名称的视图文件,我认为您的文件名不能包含空格或 & 符号。

res.render 的第二个参数是您想让视图模板可用的变量。仅使用这两个参数调用res.render 会将渲染视图隐式发送到客户端。除非您确实需要获取渲染视图的字符串版本以在服务器上使用,否则无需传入回调函数作为第三个参数。

next 是您从中间件函数调用的函数,让 express 知道您已准备好让它继续沿中间件管道向下运行。您实际上并不需要中间件函数,但如果您这样做了,那么您捕获的任何错误都应该作为第一个参数传递给 next 函数。 Express 知道,如果您将值传递给 next,那么它应该跳转到 express 错误处理程序并为用户呈现错误消息视图。

我相当肯定,在中间件函数或最终请求处理函数中抛出错误仍然会被 express 捕获。

如果函数是异步的,则您不能永远从函数返回值。这正是您需要在任何地方传递回调函数的原因。您正在传递一个函数,以便节点可以稍后在您想要的数据准备好时调用它,然后节点可以调用您的回调并传递您要求的数据。同时,您的应用程序继续运行并能够在等待时处理其他代码。您可能想要研究 Promise,因为它们清理了回调地狱,您最终会在回调中为其他异步函数调用异步函数,从而创建一个令人困惑的侧面地狱金字塔,如下所示:

asyncFunction1(function (err, result) 
  if (err) return handleErr(err);
  asyncFunction2(function (err, result) 
    if (err) return handleErr(err);
    asyncFunction3(function (err, result) 
      if (err) return handleErr(err);
      asyncFunction4(function (err, result) 
        if (err) return handleErr(err);
        asyncFunction5(funciton (err, result) 
          if (err) return handleErr(err);
          // do something useful
        );
      );
    );
  );
);

Promises 将使该代码更具可读性。 Promise 也使您不必检查每个回调 中的错误。查找它们;)。下面是使用 Promise 的相同代码的示例:

asyncFunction1()
  .then(function (result) 
    return asyncFunction2();
  )
  .then(function (result) 
    return asyncFunction3();
  )
  .then(function (result) 
    return asyncFunction4();
  )
  .then(function (result) 
    return asyncFunction5();
  )
  .then(function (result) 
    // do something useful
  )
  .catch(function (err) 
    // Will be called if ANY of the above generate an error.
  );

这就是我目前收到的大部分反馈,您可能需要处理很多,所以我会留在那里 :)

【讨论】:

哇,太棒了。我是否需要回调文件夹中的递归调用 += recursiveFileTree(root + '/' + file);? 我完全错过了这个花絮。我什至把它带到了我的样本中:/。这个电话肯定会使事情变得更加复杂,但它是可行的。我有几个会议,但我会在几个小时内回来更新我的答案:) 我在想“这个东西给文件夹变量返回一个值,但他只是说我永远不应该返回。”我会盯着它看,看看我能不能弄明白。非常感谢。你是回调天堂里的快递神。 另外,这应该是迭代的而不是递归的吗?我认为这可能是问题的症结所在。

以上是关于在 express get() 中正确调用下一个回调的主要内容,如果未能解决你的问题,请参考以下文章

用于 $http.get() 调用的 Node Express Cross Origin

Express 的多个 GET 参数

使用 express 和 node.js 重定向 301。 GET 没有被调用

如何使用Express在NodeJS中的GET请求中进行GET请求

使用 $.get 调用文件时,socket.io 事件不起作用

如何正确地从 ReactJS + Redux 应用程序进行 REST 调用?