【问题标题】:Proper calling of next callback in express get()在 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是异步的。

标签: javascript node.js express next


【解决方案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);?
  • 我完全错过了这个花絮。我什至把它带到了我的样本中:/。这个电话肯定会使事情变得更加复杂,但它是可行的。我有几个会议,但我会在几个小时内回来更新我的答案:)
  • 我在想“这个东西给文件夹变量返回一个值,但他只是说我永远不应该返回。”我会盯着它看,看看我能不能弄明白。非常感谢。你是回调天堂里的快递神。
  • 另外,这应该是迭代的而不是递归的吗?我认为这可能是问题的症结所在。
猜你喜欢
  • 2018-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-17
相关资源
最近更新 更多