Express + Sqlite3:如何让SQL语句按指定顺序执行?
Express + Sqlite3:如何让SQL语句按指定顺序执行?
嗨,我完全懂你遇到的这个坑!这其实是Node.js异步操作的典型问题——SQLite3的db.run()方法是异步非阻塞的,你用forEach遍历执行两个语句时,forEach会同步把两个db.run()调用都发出去,但数据库处理这两个操作的顺序是不确定的,谁先完成全看数据库调度,所以才会出现插入操作比建表先执行完的情况;而手动执行时是同步逐条运行,自然没问题。
下面给你两种靠谱的解决方案,都能严格保证SQL语句的执行顺序:
方案一:用SQLite3自带的serialize()方法(最推荐)
SQLite3的Node.js API专门提供了db.serialize()方法,它会把内部的所有数据库操作放进一个队列,前一个操作执行完成后才会启动下一个,完美解决顺序问题。修改你的代码如下:
const express = require("express"); const cors = require("cors"); const sqlite3 = require("sqlite3").verbose(); const app = express(); app.use(cors()); let db = new sqlite3.Database("./database.db", err => { if (err) { console.error(err.message); } else { console.log("Successful connection to the database"); // 连接成功后再执行SQL语句 executeSqlStatements(); } }); const statements = [ "CREATE TABLE IF NOT EXISTS Products (product TEXT, price INTEGER);", "INSERT INTO Products (product, price) VALUES ('Apple', 5.99);" ]; function executeSqlStatements() { db.serialize(() => { statements.forEach(statement => { db.run(statement, err => { if (err) { console.error(`${statement} failed: ${err.message}`); } else { console.log(`${statement}: successful!`); } }); }); }); } app.listen(3000, () => { console.log("Connection: http://localhost:3000/") });
这样修改后,建表语句一定会先执行完成,再启动插入操作,你就能看到预期的执行顺序了。
方案二:用Promise + Async/Await手动控制顺序
如果你想自己掌控异步流程,可以把db.run()封装成Promise,再用async/await逐条执行:
const express = require("express"); const cors = require("cors"); const sqlite3 = require("sqlite3").verbose(); const app = express(); app.use(cors()); let db = new sqlite3.Database("./database.db", err => { if (err) { console.error(err.message); } else { console.log("Successful connection to the database"); executeSqlStatements().catch(err => console.error(err)); } }); const statements = [ "CREATE TABLE IF NOT EXISTS Products (product TEXT, price INTEGER);", "INSERT INTO Products (product, price) VALUES ('Apple', 5.99);" ]; // 把db.run封装成Promise function runDbStatement(db, statement) { return new Promise((resolve, reject) => { db.run(statement, err => { if (err) reject(new Error(`${statement} failed: ${err.message}`)); else resolve(statement); }); }); } // 用async函数按顺序执行 async function executeSqlStatements() { for (const stmt of statements) { // 这里不能用forEach,必须用for...of配合await const result = await runDbStatement(db, stmt); console.log(`${result}: successful!`); } } app.listen(3000, () => { console.log("Connection: http://localhost:3000/") });
这个方法的核心是用await等待每个语句执行完成后,再执行下一条,同样能保证顺序。
另外要提醒你:原来代码里的try/catch是无效的,因为db.run()的回调是异步的,try块已经执行完毕,回调里的错误根本抓不到,所以要在回调里直接判断err对象,或者用Promise的catch处理错误。
备注:内容来源于stack exchange,提问作者bascott
相关产品推荐
相关产品推荐

