javascript - 在接受服务器请求之前等待种子数据

标签 javascript node.js mongodb express mongoose

正如标题所暗示的,这里的问题是在加载基本根目录后运行的 server.js 中的一个函数。下面你可以看到函数调用和根。

seedDB();
app.get("/",function(req,res)
{
    examBoard.find({}, function (err, examBoard)
    {
        console.log("examBoard.length: " + examBoard.length);
        res.render("landing", { examBoard: examBoard });
    });
});

该函数执行数据库的基本播种,因此必须在基本根之前运行。它输出您在下图中可以看到的内容(大部分输出被切断)。

enter image description here

红框中的输出是 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函数必须在基本根之前运行,如果您可以提供解决方案或仅将我指向正确的方向,将不胜感激。

最佳答案

底线是您的 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() );
  });

}

所以它基本上是一样的,除了我们“明显地链接” promise 而不是使用 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 ,那么你总是可以将它们“包装”在 Promise 中,就像 user.register 一样。假设它是“仅回调”。实际实现可能会有所不同,但这是通过演示包装回调的常用方法。

归根结底,一切都在正常等待,整个功能只有在一切都完成后才能解决。这使您可以继续进行重要的部分。

实现等待服务器启动

使种子函数成为“异步”返回的全部原因是我们知道它何时完成,然后只会调用 .listen()确认后的方法,因此服务器在数据准备好之前不会接受请求。

同样,根据可用的 Node 版本,您可以使用 async/await语法或链接要解决的 promise 。

所以使用 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 中列出。 block ,它将捕获任何错误而没有所有其他回调困惑。

或者,“链接”方法是:
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 都是“快速失败”的,因为任何错误基本上都将在 try/catch 中被捕获。或 .catch()分别,而不试图继续。在大多数情况下,这就是您想要的,但您可以交替使用类似的 block 或 .catch() 来处理它。细粒度区域的处理程序。

设计说明

因此,提出的问题有一些非常困惑的代码,其中包含大量回调嵌套,此处展示的更现代的功能旨在“清理”并使事物再次具有功能性和可读性。您应该立即注意到理解上的显着差异。

在我看来,仍然存在问题。主要的例子是“所有问题都被添加到所有内容中”,这对于测试目的来说可能没问题,但我怀疑这是你的意图。可能有很多更有效的方法来连接这些问题,但这基本上偏离了问题的主要主题,即“等待异步完成”。

因此,部分代码有一些变化,但仅限于实现的代码实际上没有做任何事情的区域,或者至少没有做任何你可能期望的事情。 ( topicIncrementor 只会暴露给初始化为 0 的循环,并且永远不会不同)。

总体而言,这应该为您提供一个合理的指导,以了解如何以不同的方式完成事情,并了解这些任务的简洁程度,以便它们读起来完全符合它们的设计目的,而不是看起来更像“功能面”。

关于javascript - 在接受服务器请求之前等待种子数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45929426/

相关文章:

javascript - 如何为背景网格行添加自定义删除选项

javascript - 使用 nunjucks 或其他方法的嵌套变量的模板解决方案

javascript - 根据选项选择隐藏或显示 div

node.js - 文件上传前生成目标路径 - multer

java - Mongo Java 驱动程序通过文本索引搜索

javascript - 使用函数作为对象的键

node.js - fatal error : CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory in production

javascript - Grunt 找不到任务

javascript - 错误 [ERR_HTTP_HEADERS_SENT] : Cannot set headers after they are sent to the client in Node JS

php - 使用 Li3 为 MongoDB 设置安全 => 'majority'