javascriptnode.jsserializationconcurrencynode-sqlite3

Force instance of Database using all() in serial to update global variable


I am learning sqlite3 in Node.js and having problem with following code

var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('first.db');

console.log("in the program");
var x = [];
console.log("x = ", x);

console.log("db.all()");
db.all("SELECT * FROM coords",
  function(err, rows) {
    console.log("\nin db.all()");
    for(var row of rows) {
      x.push(row.x);
      console.log(x);
    }
  }
);

db.close();

console.log("x = ", x);

where the output is

> node dball.js
in the program
x =  []
db.all()
x =  []

in db.all()
[ 1 ]
[ 1, 5 ]
[ 1, 5, 9 ]

It seems that all lines outside db.all() are executed first than the others inside it, so that array of x seems not changed, since it is printed before it is updated.

Is there any way force all lines are executed in serial instead sort of concurrent? I believe I am missing the concept of concurrency here but not know how to do.

Thank's for any help.


Solution

  • db.all is a function that takes 2 inputs: the query as a string and a callback function which will be called later when the database managed to run the query and have the results (or an error) ready to report back to you. I suggest you read more about the nature of asynchronous code in JavaScript: callbacks, promises, async/await, etc. Also, we no longer use var to declare variables.

    That being said, what you can actually do is to "promisify" db.all function and then use async/await to make it look synchronous (it will still be async code).

    const sqlite3 = require('sqlite3').verbose();
    const db = new sqlite3.Database('first.db');
    
    function getData(query) {
      // promisifying
      return new Promise((resolve, reject) => {
        db.all(query, (err, rows) => {
          if (err) {
            reject(err);
            return;
          }
          
          resolve(rows);
        }); // db.all
      }); // new Promise
    } // getData
    
    // main is needed because some JS engines don't allow top level "await"
    // so I am playing safe here
    async function main() {
      console.log("in the program");
      const x = [];
      console.log("x = ", x);
    
      console.log("db.all()");
     
      try {
        // the nice thing is that you don't even need `x` now because
        // you can just do something with `rows`
        const rows = await getData("SELECT * FROM coords");
        for(var row of rows) {
          x.push(row.x);
          console.log(x);
        }
      } catch (err) {
        // do something in case of error
      } finally {
        // this is always executed regardless of error or not
        // so it is a good place to close the database
        db.close();
      }
    
      console.log("x = ", x);
    }
    
    // make sure to call main
    main();