当一个页面有多个数据块的时候,后台一般是怎么写的? Nodejs 相关实现思路
当一个页面有多个数据块的时候,后台一般是怎么写的? Nodejs 相关实现思路
是挨个嵌套调用吗,然后在最内层render 模板吗?
当一个页面包含多个数据块时,后台处理通常会涉及到多个异步操作,这些操作可能包括数据库查询、外部API调用等。在Node.js中,可以使用Promise或async/await来管理这些异步操作,确保所有数据都准备好后再渲染模板。
以下是一个简单的示例,展示如何使用Node.js和Express框架来处理这种情况:
-
安装必要的依赖:
npm install express
-
创建服务器文件(例如
app.js
):const express = require('express'); const app = express(); const port = 3000; // 假设我们有两个数据源 const fetchDataBlockA = async () => { return new Promise((resolve) => { setTimeout(() => { resolve({ blockA: 'Data from Block A' }); }, 500); }); }; const fetchDataBlockB = async () => { return new Promise((resolve) => { setTimeout(() => { resolve({ blockB: 'Data from Block B' }); }, 300); }); }; // 渲染模板的函数 const renderTemplate = (data) => { const template = ` <html> <head><title>Data Blocks</title></head> <body> <h1>Data Block A</h1> <p>${data.blockA}</p> <h1>Data Block B</h1> <p>${data.blockB}</p> </body> </html> `; return template; }; app.get('/', async (req, res) => { try { // 同时获取两个数据块 const [dataA, dataB] = await Promise.all([fetchDataBlockA(), fetchDataBlockB()]); // 合并数据 const combinedData = { ...dataA, ...dataB }; // 渲染模板 const htmlContent = renderTemplate(combinedData); res.send(htmlContent); } catch (error) { console.error(error); res.status(500).send('Internal Server Error'); } }); app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });
解释
-
数据获取:
fetchDataBlockA
和fetchDataBlockB
是两个异步函数,分别模拟从不同数据源获取数据的过程。- 使用
setTimeout
来模拟异步操作,实际应用中可能会涉及数据库查询、外部API调用等。
-
模板渲染:
renderTemplate
函数用于将获取到的数据合并到HTML模板中。
-
路由处理:
- 在根路径
/
的路由处理器中,使用Promise.all
同时获取两个数据块,并在数据准备好后调用renderTemplate
函数生成最终的HTML内容。 Promise.all
确保所有异步操作完成后才继续执行后续逻辑,避免了嵌套回调带来的复杂性。
- 在根路径
这种方式不仅提高了代码的可读性和可维护性,还使得异步操作更加高效和易于管理。
多个数据块之间有依赖吗?
平行关系的多个数据块,再模板里单独render就行。
比如这个贴子的页面,左面是贴子,右面是用户信息, 两者没有依赖关系,你说的单独render是这样吗?
var post = require('../models/post.js');
post.findOne({_id:xxx},function(err,post)){
res.render(‘topic’,{post:post});
}
var user = require(’…/models/user.js’);
post.findOne({_id:xxx},function(err,info)){
res.render(‘topic’,{info:info});
}
肯定不是这样的啊,如果是ejs模板,你可以用include ,你要等到把数据都拿到了才调用render,或者前头异步加载
这个问题抽象出来就是nodejs的异步流控制(Asynchronous Control Flow),楼主所关心是属于其中的并行流。成熟的解决方案有async,step,promise之类的,自行google。顺带一提,cnodejs的社区论坛是用eventproxy。 如果不想引入额外的模块,自行折腾一个也不难。论坛这篇文章JS异步流程控制(序列模式、并发模式、有限并发模式)应该对你有所启发。考虑伸手党,附上我自己曾经用过一种方法的:
function parallel(limit, done) {
if (typeof limit == 'function') {
done = limit, limit = 0;
}
var jobs = [], results = [], i = 0;
var dones = 0; //total number of finished job
function next(n) {
if (dones === jobs.length) done(results);
for (var j = 0; j < n && i < jobs.length; j++, i++) {
jobs[i](function (pos) {
dones++;
results[pos] = [].slice.call(arguments, 1);
next(1);
}.bind(null, i));
};
}
process.nextTick(function () {
//run next <limit> jobs. if limit=0, run all the jobs
next(limit || jobs.length);
});
return {
do : function (job) { jobs.push(job); return this; }
};
}
var post, info;
var task = parallel(function (results) {
res.render('topic', { post: post, info: info });
});
task.do(function (done) {
post.findOne({ _id: xx }, function (err, _post) {
post = _post; done();
});
});
task.do(function (done) {
post.findOne({ _id: xx }, function (err, _info) {
info = _info; done();
});
});
在Node.js中处理多数据块的情况时,通常不会采用嵌套调用的方式来获取所有数据,而是使用异步编程来并行或按需获取数据。这可以通过Promise、async/await或者回调函数来实现。这里提供一种较为简洁的方法,即利用Promise.all
来并行处理多个异步操作,并且在所有数据获取完成后统一渲染模板。
实现思路
- 定义获取数据的函数:为每个数据块定义一个独立的函数,这些函数负责与数据库交互或其他服务进行通信以获取特定的数据。
- 使用Promise.all并行处理:使用
Promise.all
来并行执行所有数据获取函数。这样可以提高效率,尤其是在数据获取之间没有依赖关系的情况下。 - 渲染模板:在所有数据获取完成之后,将所有数据传递给模板引擎进行渲染。
示例代码
假设我们有一个Express应用,并且需要从两个不同的数据源获取数据(比如一个用户列表和一篇博客文章),我们可以这样做:
const express = require('express');
const app = express();
const fetchData1 = require('./fetchData1'); // 假设这是获取第一个数据块的函数
const fetchData2 = require('./fetchData2'); // 获取第二个数据块的函数
app.get('/page', async (req, res) => {
try {
const [data1, data2] = await Promise.all([fetchData1(), fetchData2()]);
res.render('pageTemplate', { data1, data2 });
} catch (error) {
console.error(error);
res.status(500).send('Server error');
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
在这个例子中,fetchData1()
和 fetchData2()
是两个异步函数,它们分别负责从不同的数据源获取数据。Promise.all
确保这两个函数并发执行,且只有当两个都成功返回结果后,才会调用res.render
渲染页面。
这种方式不仅使代码更加清晰,而且提高了服务器性能,因为它允许同时加载多个数据块。