正如标题所示,这里的问题是在加载基根之后运行的server.js中的一个函数。您可以在下面看到函数调用和根。
seedDB();
app.get("/",function(req,res)
{
examBoard.find({}, function (err, examBoard)
{
console.log("examBoard.length: " + examBoard.length);
res.render("landing", { examBoard: examBoard });
});
});
该函数执行数据库的基本种子,因此必须在基本根之前运行。它会输出您在下图中看到的内容(大部分输出都被截断)。
红色框中的输出是基础根中console.log
的结果。这是app.listen
,位于代码的最底部,其上方包含所有内容。
app.listen(process.env.PORT, process.env.IP,function()
{
console.log("Server started");
});
以下是seedDB
的代码,其中包含完整代码,包括此hastebin链接中的数组(https://hastebin.com/acecofoqap.lua)(认为包含它们有点过分,因为它们相当大):
function seedDB() {
user.remove({}, function (err) {
if (err) {
console.log("Could not remove user\n" + err);
}
else {
console.log("Removed old user");
examBoard.remove({}, function (err) {
if (err) {
console.log("Could not remove examboards\n" + err);
}
else {
console.log("Removed old examboards");
question.remove({}, function (err) {
if (err) {
console.log("Could not remove questions\n" + err);
}
else {
console.log("Removed old questions");
user.register(new user
({
username: "admin",
email: "jonathanwoollettlight@gmail.com",
role: "admin"
}),
"lu134r7n75q5psbzwgch", function (err, user) {
if (err) {
console.log("Failed to add admin\n" + err);
}
else {
console.log("Admin added");
examboardData.forEach(function (examSeed) {
examBoard.create(examSeed, function (err, exam) {
console.log("Creating new examboard");
if (err) {
console.log("Could not create new examboard\n" + err);
}
else {
console.log("Created examboard");
}
});
});
var topicIncrementor = 0;
questionData.forEach(function (questionSeed) {
question.create(questionSeed, function (err, question) {
if (err) {
console.log("Could not create new question\n" + err);
}
else {
console.log("Created question");
examBoard.find({}, function (err, exams) {
for (var i = 0; i < exams.length; i++) {
for (var t = 0; t < exams[i].modules.length; t++) {
for (var q = math.floor(topicIncrementor / 12); q < exams[i].modules[t].topics.length; q++) {
exams[i].modules[t].topics[q].questions.push(question);
topicIncrementor++;
}
topicIncrementor = 0;
}
exams[i].save();
}
});
}
});
});
}
});
}
});
}
});
}
});
}
module.exports = seedDB;
为了让我的程序在这里工作,seedDB
函数必须在基本根之前运行,如果你能提供解决方案或只是指出我正确的方向,那将非常感激。
答案 0 :(得分:1)
底线是你的seedDB()
需要异步回调或Promise解析本身,然后在该操作完成时甚至只启动服务器的'http'部分。原因是服务器在确认要加载数据之前甚至不接受请求。
使用nodejs的现代版本,最简单的实现方法是使用async/await
语法
async function seedDB() {
// Remove existing data
await Promise.all(
[user,examBoard,question].map( m => m.remove({}) )
);
// Create the user, wrap callback method with Promise
await new Promise((resolve,reject) => {
user.register( new user({
username: "admin",
email: "jonathanwoollettlight@gmail.com",
role: "admin"
}),"lu134r7n75q5psbzwgch", (err, user) => {
if (err) reject(err);
resolve(user);
});
});
// Create examBoard. .create() does actually accept an array.
// Keep the results as well
var exams = await examboard.create(examboadData);
// Create questions. Same thing
var questions = question.create(questionData);
// Add questions to each exam topic
for ( let question of questions ) {
for ( var i = 0; i < exams.length; i++ ) {
for ( var t = 0; t < exams[i].modules.length; t++ ) {
for ( var q = 0; q < exams[i].modules[t].topics.length; q++ ) {
exams[i].modules[t].topics[q].questions.push(question);
}
}
await exams[i].save();
}
}
}
稍微回过头来看看普通Promise
实现的外观:
function seedDB() {
// Remove existing data
return Promise.all(
[user,examBoard,question].map( m => m.remove({}) )
)
.then( () =>
// Create the user, wrap callback method with Promise
new Promise((resolve,reject) => {
user.register( new user({
username: "admin",
email: "jonathanwoollettlight@gmail.com",
role: "admin"
}),"lu134r7n75q5psbzwgch", (err, user) => {
if (err) reject(err);
resolve(user);
});
})
)
.then( () =>
Promise.all(
// Create examBoard. .create() does actually accept an array.
// Keep the results as well
examboard.create(examboadData),
// Create questions. Same thing
question.create(questionData)
)
)
.then( ([exams, questions]) => {
// Add questions to each exam topic
var items = [];
for ( let question of questions ) {
for ( var i = 0; i < exams.length; i++ ) {
for ( var t = 0; t < exams[i].modules.length; t++ ) {
for ( var q = 0; q < exams[i].modules[t].topics.length; q++ ) {
exams[i].modules[t].topics[q].questions.push(question);
}
}
items.push(exams[i].save().exec());
}
}
return Promise.all(items).then( () => Promise.resolve() );
});
}
所以它基本上是相同的,除了我们“明显地链接”承诺而不是使用可以在现代环境中使用的await
糖。
这里要理解的关键是“mongoose API中的所有东西都返回一个Promise”,因此有更简洁的方法来实现等待完成和链接调用。
这适用于.remove()
来电的简单链接:
await Promise.all(
[user,examBoard,question].map( m => m.remove({}) )
);
通过“循环所有已注册的模型”来“播种”初始数据时,我通常会写一些内容:
await Promise.all(
Object.keys(conn.models).map( m => conn.models[m].remove({}) )
)
或者如果有些方法实际上没有承诺,那么你总是可以在Promise中“包装它们”,就像user.register
假设它是“仅回调”一样。实际实现可能有所不同,但这是通过演示包装回调的常用方法。
在一天结束时,一切都正常等待,整个功能只有在一切都完成后才能解决。这使您可以转到重要部分。
使种子函数成为“异步”返回的全部原因是我们知道它何时完成,然后只有在确认后才调用.listen()
方法,因此服务器根本不接受请求数据准备就绪。
同样,根据可用的节点版本,您可以使用async/await
语法或链接承诺来解析。
因此,使用async/await
,您的主服务器启动应该如下所示:
const mongoose = require('mongoose'),
app = require('express')();
// and other module includes, including the seedDB source
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
mongoose.Promise = global.Promise;
mongoose.set('debug',true); // useful before deployment to see requests
// Possibly other express handler set up
// Root handler
app.get('/', function(req,res) {
examBoard.find({}, function (err, examBoard) {
console.log("examBoard.length: " + examBoard.length);
res.render("landing", { examBoard: examBoard });
});
});
// Main startup
(async function() {
try {
const conn = await mongoose.connect(uri,options);
// Seed and await
await seedDB();
// Then we listen
await app.listen(5858);
console.log('server listening');
} catch(e) {
console.error(e);
mongoose.disconnect(); // Probably not realistically
}
})();
此处async/await
suguar允许您按顺序列出每个操作,也可以在try/catch
块中列出任何错误,而不会导致所有其他回调混乱。
或者,“链接”方法是:
const mongoose = require('mongoose'),
app = require('express')();
// and other module includes, including the seedDB source
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
mongoose.Promise = global.Promise;
mongoose.set('debug',true); // useful before deployment to see requests
// Possibly other express handler set up
// Root handler
app.get('/', function(req,res) {
examBoard.find({}, function (err, examBoard) {
console.log("examBoard.length: " + examBoard.length);
res.render("landing", { examBoard: examBoard });
});
});
// Main startup
mongoose.connect(uri,options)
.then( () => seedDB() )
.then( () => app.listen(5858) )
.then( () => console.log('server listening') )
.catch( e => console.error(e) );
除了“链接”之外,我们在链的末尾使用.catch()
,这真的唯一不同。
请注意,在所有情况下,此处的promise都是“fail-fast”,因为任何错误基本上都会分别被try/catch
或.catch()
捕获,而不会尝试继续。在大多数情况下,这就是你想要的,但你可以通过在更细粒度的区域使用类似的块或.catch()
处理程序来交替处理它。
所以提出的问题有一些非常混乱的代码,有很多回调嵌套,这里展示的更现代的功能意味着“清理”并使事物再次具有功能性和可读性。你应该立即注意到理解上的显着差异。
我认为,仍有问题。主要的例子是“所有问题都被添加到所有内容中”,这可能适用于测试目的,但我怀疑这是你的意图。并且可能有更多有效的方法来加入这些问题,但它基本上不是问题的主题,只是“等待异步完成”。
因此,代码的某些部分会有一些变化,但仅限于实现的代码实际上没有做任何事情的地方,或者至少没有做任何您可能期望的事情。 (topicIncrementor
只会暴露于循环初始化为0
,并且永远不会有所不同)。
总的来说,这应该为你提供一个合理的指导,让你理解如何以不同的方式完成工作,以及看看这些任务是多么干净和简单,这样他们就可以完全按照他们的设计来阅读,而不是寻找更像是“功能性面条”。