
本文详解如何使用 async/await 和 promise 封装 sqlite3(及兼容方案)的数据库操作,解决因异步流程失控导致的“connection was never established”等常见错误,确保建表、插入、查询严格按序执行。
本文详解如何使用 async/await 和 promise 封装 sqlite3(及兼容方案)的数据库操作,解决因异步流程失控导致的“connection was never established”等常见错误,确保建表、插入、查询严格按序执行。
在 Node.js 中操作嵌入式 SQL 数据库(如 sqlite3 或 duckdb)时,一个典型陷阱是误将异步操作当作同步执行。原始代码中直接调用 createAndStoreData() 后立即执行 readData(),而二者内部均涉及文件 I/O、SQL 编译与执行、连接关闭等异步过程——但未做任何等待机制,导致 readData() 在数据库尚未写入完成甚至尚未关闭时就尝试打开并查询,从而抛出 Connection Error: Connection was never established or has been closed already。
根本原因在于:sqlite3 的 .run()、.all()、.close() 等方法本身不返回 Promise,而是通过回调函数通知完成状态;若不显式封装为 Promise,就无法用 await 控制执行时序。
✅ 正确做法是:将每个数据库操作封装为返回 Promise 的异步函数,并在主流程中使用 await 串行调用。以下是以 sqlite3 为例的完整可运行方案(兼容性强,推荐初学者优先采用):
const sqlite3 = require('sqlite3').verbose();
const csvParser = require('csv-parser');
const stream = require('stream');
const csvData = `Wind speed (m/s),Output power (kW)
0,0
1,0
2,0
3,0
4,80
5,140
6,360
7,610
8,1000
9,1470
10,1900
11,2320
12,2690
13,2850
14,2950
15,3000
16,3000
17,3000
18,3000
19,3000
20,3000
21,3000
22,3000
23,3000
24,3000
25,3000`;
// ✅ 封装为 Promise:创建表 + 批量插入
async function createAndStoreData() {
console.log('→ 正在初始化数据库并导入数据...');
const db = new sqlite3.Database('power_curve.db');
return new Promise((resolve, reject) => {
db.serialize(() => {
// 确保表结构干净
db.run('DROP TABLE IF EXISTS power_curve', (err) => {
if (err) return reject(err);
});
db.run('CREATE TABLE power_curve (ws REAL PRIMARY KEY, power REAL)', (err) => {
if (err) return reject(err);
});
const stmt = db.prepare('INSERT INTO power_curve (ws, power) VALUES (?, ?)');
const dataStream = new stream.Readable();
dataStream.push(csvData);
dataStream.push(null);
dataStream
.pipe(csvParser())
.on('data', (row) => {
stmt.run(
parseFloat(row['Wind speed (m/s)']),
parseFloat(row['Output power (kW)'])
);
})
.on('end', () => {
stmt.finalize();
db.close((err) => {
if (err) reject(err);
else {
console.log('✔ 数据已成功存入 power_curve.db');
resolve();
}
});
})
.on('error', reject);
});
});
}
// ✅ 封装为 Promise:查询全部数据
async function readData() {
console.log('→ 正在从数据库读取数据...');
const db = new sqlite3.Database('power_curve.db');
return new Promise((resolve, reject) => {
db.all('SELECT * FROM power_curve ORDER BY ws', (err, rows) => {
if (err) {
db.close();
return reject(err);
}
console.table(rows);
console.log('✔ 数据读取完成');
db.close((closeErr) => {
if (closeErr) reject(closeErr);
else resolve();
});
});
});
}
// ✅ 主流程:严格顺序执行
async function main() {
try {
await createAndStoreData(); // 等待写入完成
await readData(); // 再执行读取
} catch (err) {
console.error('❌ 操作失败:', err.message);
process.exit(1);
}
}
if (require.main === module) {
main();
}? 关键要点总结:
立即学习“Java免费学习笔记(深入)”;
-
永远不要忽略回调时机:
sqlite3的.close()必须在所有.run()/.all()完成后调用,且需等待其回调触发才真正释放资源; -
类型安全建议:CSV 字段应显式
parseFloat()转换,避免字符串写入导致后续数值查询异常; -
DuckDB 注意事项:当前 DuckDB for Node.js(非 WASM 版)API 与
sqlite3不完全兼容(如无.serialize(),.prepare()行为不同),若坚持使用 DuckDB,请参考其官方文档采用db.exec()+await db.insert()等现代异步接口,或改用@duckdb/duckdb-wasm的 Promise-first API; -
生产增强建议:添加事务(
BEGIN/COMMIT)、错误重试、连接池(如sqlite3-pool)以提升健壮性。
通过 Promise 封装 + async/await 驱动,你就能彻底告别“数据库未就绪却强行读取”的竞态问题,让 SQL 操作真正可控、可预测、可维护。


















