SQLite3の非同期処理を同期化させる(Node.js)

便利なようで結構不便なJavaScriptのシングルタスクを有効に使おうとするイベント処理。非同期処理は簡単な使い方では不便でSQLite3のDBアクセスライブラリには同期型も存在するらしい。

ユーザーが多いだろうから非同期型のライブラリでなんとかしようとするとPromise処理が必要になります。最新の機能ではawait/asyncになりますが。以下はexpress-generatorで発生されたwebページのディスパッチ処理を行うindex.jsにデータベースアクセス処理を同期させて追加したコードです。

<index.js>

var express = require('express');
var router = express.Router();

let nameArray = new Array();

/* GET home page. */
router.get('/', function(req, res, next) {
  var response = Math.floor(Math.random() * (100 - 1)) + 1;

  let t1 = new Date();

async function dbReadExec(){
  try{
    nameArray = await dbRead();
    let t2 =new Date();
    console.log("dbReadEec",nameArray, t2 - t1);
    res.render('index', { title: 'Express', data: response, db: nameArray});
  } catch (err){
    console.log('error');
  } finally {
    console.log("done");
  }
}

dbReadExec();

});

//
// DB handler
//
function dbRead(){
  return new Promise((resolve, reject) => {

  const sqlite3 = require("sqlite3");
  const db = new sqlite3.Database("/Users/username/go/src/serial/myfare.db");
  
  // calc file name
  let d = new Date();
  let year = d.getFullYear();
  let month = d.getMonth();
  let day = d.getDate();
  
  let date1 = new Date(year, 0, 0);
  let date2 = new Date(year, month, day)
  let behind = Math.floor((date2 - date1) / (24*3600*1000));      // convert milisec to day
  
  nameArray = [];       // clear Array
  
  db.serialize(() => {
  db.each("select * from tbl" + year + behind + " where stat=0", (err, row) => {
    if (err) {
      reject(err);
    }
  //console.log(`${row.id} ${row.name} ${row.time} ${row.stat}`);    
  nameArray.push(row.name);
  resolve(nameArray);
  })
  
  db.close();

  });
  });
}


module.exports = router;

serialize()したつもりでも、resolve(nameArray);をdb.close()の後にするとうまく動きません。ここも非同期で動くから?ループ内でresolve()を複数回発行することになりますが、これはthenがchainされて最後のresolve()が有効になるようです。データ量が少ないなら、db.each()ではなくdb.all()で一回で返せば良さそうに思いますが。

let t2 =new Date();

console.log("dbReadEec",nameArray, t2 - t1);

はデータベース処理時間計測のためのタイムスタンプです。二回目以降はJavaScriptのキャッシュが有効だろうから高速です。

シングルタスク言語というのはちょっと複雑な構造のコードを書こうとすると不便なところが目立つように感じます。

 

P.S. 2023/3/28

db.each部分だけをdb.allに置き換え、

  db.all("select * from tbl" + year + behind + " where stat=0", (err, rows) =>{
    rows.forEach(row => nameArray.push(row.name));
    //console.log(nameArray);
    resolve(nameArray);
})

こちらの方がスマートでしょう。

 

admin