【问题标题】:NodeJS wait for variable to be assigned from sql.get before continuingNodeJS 在继续之前等待从 sql.get 分配变量
【发布时间】:2017-12-17 10:16:27
【问题描述】:

我知道 sqlite(不是 sqlite3)模块会批量请求。我需要从数据库中获取一个值,分配它,然后对其进行一些处理。但是,从数据库中获取值的函数在所有处理完成后才会返回。

我需要发生以下三件事之一:

  1. 立即返回函数
  2. 暂停代码,直到分配了 myEvent.id
  3. 让 sql.get 立即返回

    myEvent.id = generateEventID();
    ///do stuff with myEvent.id
    
    function generateEventID() {
    return sql.get('SELECT * FROM settings WHERE name = "eventID"').then(row => {
      if (!row) return message.reply("a database error occured while generating an ID");
      currentID = row.intValue + 1;
      console.log("New eventID created: " + currentID);
      sql.run(`UPDATE settings SET intValue = ${row.intValue + 1} WHERE name =  "eventID"`);
      return currentID;
      });
    }
    

【问题讨论】:

标签: node.js sqlite asynchronous async-await


【解决方案1】:

很遗憾,您不能直接从函数返回 Id,因为 sql.get(...) 是异步的。

解决此问题的一种可能方法是将callback 函数作为参数传递给generateEventID(),然后让调用者等待callback 函数被调用,然后再继续进行。

因此你会看起来像这样。

var doStuffWithEventId = function(eventId) {
    // Do more stuff.
}

generateEventID(doStuffWithEventId);


function generateEventID(callback) {
    return sql.get('SELECT * FROM settings WHERE name = "eventID"').then(row => {
        if (!row) return message.reply("a database error occured while generating an ID");
        currentID = row.intValue + 1;
        console.log("New eventID created: " + currentID);
        sql.run(`UPDATE settings SET intValue = ${row.intValue + 1} WHERE name =  "eventID"`);
        return callback(currentID); // Call function doStuffWithEventId with value of currentID variable
    });
}

这是完成您想做的事情的一种方式。

此处附注。

  1. 您通常会在返回任何内容之前等待 update 操作完成。
  2. 您预计 Id 将始终是 1 的增量。数据库并不总是这样做。所以你可能会措手不及。
  3. 您的代码假定数据库操作将始终成功。您通常希望处理在进行外部调用时可能引发的潜在异常。

【讨论】:

    【解决方案2】:

    如您所说,您的代码应按特定顺序提供三个主要功能:

    1. 从您的数据存储(本例中为 SQL 数据库)中获取结果。
    2. 对获得的数据执行特定操作(称为 //do more stuff在你的片段中)
    3. 对您的数据存储执行UPDATE 查询。

    因此,您提供的实现逻辑是错误的,因为它无法按特定顺序执行这三个操作。不要忘记默认情况下所有 Node 调用都是异步,在这种情况下,您无法强制执行所需的同步。要解决此问题,您应该执行以下步骤:

    1. 从数据存储中获取结果。 (已经完成)。
    2. 对获得的数据执行特定操作。这必须在 sql.get() 回调中完成,或者在 Promise 解决后完成(因为在这种情况下您使用的是 Promise)。
    3. 对您的数据执行操作的方法(即第 2 步)应该返回一个新的承诺。一旦这个承诺得到解决,您将能够调用您的UPDATE 操作,这样您将确保您的实现将满足您的所有要求。

    现在,我会这样做:

    generateEventID();
    
    function doMoreStuff(eventId){
      return new Promise(function(resolve, reject){
        //do your stuff
        if(err){
          reject(err)
        }else{
          resolve()
        }
      })
    }
    
    function generateEventID() {
      return sql.get('SELECT * FROM settings WHERE name = "eventID"').then(row => {
        if (!row) return message.reply("a database error occured while generating an ID");
        currentID = row.intValue + 1;
        console.log("New eventID created: " + currentID);
        doMoreStuff(currentID).then(function(){ //At this point your operations with currentId will have finished already
          sql.run(`UPDATE settings SET intValue = ${row.intValue + 1} WHERE name =  "eventID"`);
        });
      });
    }
    

    我真的希望这会有所帮助!

    编辑:不要忘记使用.catch() 实现对承诺的错误处理

    【讨论】:

    • 我不确定您所说的 node calls 是什么。我怀疑您是在说“函数调用”。如果这是真的,那么它是不正确的。并非每个函数调用都是async。如果每个函数调用都返回一个 Promise,你会发现由于不必要的上下文切换,应用程序的运行速度会比平时慢很多。
    • 我的意思是节点 I/O 调用,因为在这种情况下我们正在处理 SQL 查询。抱歉我忘了提,但是这些 I/O 调用是由服务线程以异步方式处理的。
    猜你喜欢
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-14
    相关资源
    最近更新 更多