SQLite на Node.js с async/await
Интерфейс для синхронизации каждой функции SQLite и их использования с await.
Требуется модуль SQLite 3 для Node и Node.js 8.0, поддерживающий async/await.
SQLite чаще используется как средство хранения данных для локальных и мобильных приложений, также асинхронный стиль для чтения и записи в базу, и callbacks - не лучшее решение для доступа к данным в разных частях программы.
Чтобы получить более естественный доступ к данным в процедурной программе, я написал интерфейс, который преобразует callbacks в обещанные, чтобы можно было использовать каждую функцию с зарезервированным словом await .
Это не альтернатива аснихонному режиму, а скорее надстройка: можно использовать асинхронные и синхронные функции вместе.
Модуль aa-sql lite
Интерфейс в SQLite - это модуль с именем aa-sqlLite, его можно разместить в разделе node_modules приложения. Вот исходный код:
const sqlite3 = require('sqlite3').verbose()
var db
exports.db = db
exports.open=function(path) {
return new Promise(function(resolve) {
this.db = new sqlite3.Database(path,
function(err) {
if(err) reject("Open error: "+ err.message)
else resolve(path + " opened")
}
)
})
}
// any query: insert/delete/update
exports.run=function(query) {
return new Promise(function(resolve, reject) {
this.db.run(query,
function(err) {
if(err) reject(err.message)
else resolve(true)
})
})
}
// first row read
exports.get=function(query, params) {
return new Promise(function(resolve, reject) {
this.db.get(query, params, function(err, row) {
if(err) reject("Read error: " + err.message)
else {
resolve(row)
}
})
})
}
// set of rows read
exports.all=function(query, params) {
return new Promise(function(resolve, reject) {
if(params == undefined) params=[]
this.db.all(query, params, function(err, rows) {
if(err) reject("Read error: " + err.message)
else {
resolve(rows)
}
})
})
}
// each row returned one by one
exports.each=function(query, params, action) {
return new Promise(function(resolve, reject) {
var db = this.db
db.serialize(function() {
db.each(query, params, function(err, row) {
if(err) reject("Read error: " + err.message)
else {
if(row) {
action(row)
}
}
})
db.get("", function(err, row) {
resolve(true)
})
})
})
}
exports.close=function() {
return new Promise(function(resolve, reject) {
this.db.close()
resolve(true)
})
}
Метод get возвращает строку из базы, а все возвращает массив строк.
В случае each это сложнее, чем SQLite вызывает функцию обратного вызова для каждой строки, отвечающей условию запроса. Решение заключается в том, чтобы использовать Database.serialize для обработки их один за другим, а затем вызвать окончательный пустой метод get для решения обещанной.
Показ
В этой демонстрации приводится пример использования каждой функции aa-sql. В первой части открываем базу, добавляем таблицу и заполняем ее парой строк. Потом база закрывается, ее снова открывают, чтобы выполнить несколько синхронных запросов.
const fs = require("fs")
const sqlite = require("aa-sqlite")
async function mainApp() {
console.log(await sqlite.open('./users.db'))
// Adds a table
var r = await sqlite.run('CREATE TABLE users(ID integer NOT NULL PRIMARY KEY, name text, city text)')
if(r) console.log("Table created")
// Fills the table
let users = {
"Naomi": "chicago",
"Julia": "Frisco",
"Amy": "New York",
"Scarlett": "Austin",
"Amy": "Seattle"
}
var id = 1
for(var x in users) {
var entry = `'${id}','${x}','${users[x]}'`
var sql = "INSERT INTO users(ID, name, city) VALUES (" + entry + ")"
r = await sqlite.run(sql)
if(r) console.log("Inserted.")
id++
}
// Starting a new cycle to access the data
await sqlite.close();
await sqlite.open('./users.db')
console.log("Select one user:")
var sql = "SELECT ID, name, city FROM users WHERE name='Naomi'"
r = await sqlite.get(sql)
console.log("Read:", r.ID, r.name, r.city)
console.log("Get all users:")
sql = "SELECT * FROM users"
r = await sqlite.all(sql, [])
r.forEach(function(row) {
console.log("Read:", row.ID, row.name, row.city)
})
console.log("Get some users:")
sql = "SELECT * FROM users WHERE name=?"
r = await sqlite.all(sql, ['Amy'])
r.forEach(function(row) {
console.log("Read:", row.ID, row.name, row.city)
})
console.log("One by one:")
sql = "SELECT * FROM users"
r = await sqlite.each(sql, [], function(row) {
console.log("Read:", row.ID, row.name, row.city)
})
if(r) console.log("Done.")
sqlite.close();
}
try {
fs.unlinkSync("./users.db")
}
catch(e) {
}
mainApp()
Поскольку метод all возвращает массив, для обработки содержимого каждой строки используется forEach.
Ты можешь специально проверить в случае метода each, что каждая возвращаемая строка обрабатывается до того, как программа покажет «Done». Это было бы не так с оригинальными асинхронными методами.
Ты можешь загрузить архив, содержащий модуль aa-sql litet и демо.
Перед запуском демо еще нужно установить SQLite 3.