我正在尝试在NodeJS 11.6.x上开发一个相对简单的测试。我不是真正的开发人员,但有时会尝试进行一些编码。
我的目标是创建一个SQLite数据库,并在每次运行代码时重复一些步骤: 1.删除表(如果存在) 2.创建一个表 3.插入N行 4.计算数据库中有多少行 5.关闭数据库
我首先尝试了一种使用回调的基本方法,但无法找出一种方法来进行步骤3(插入N行)并寻找解决方案,promise async / await'pattern'听起来是完成的方法一切。
但是,在重构代码之后,第1步(删除表)没有运行,并且我仍然无法执行第3步(插入N行)并且不知道发生了什么。我也尝试使用没有运气的Promise程序包。
有人可以对此寻求帮助吗?如果可以的话,请解释和提出建议?
预先感谢
编辑:嗯,我不习惯在SO上发布内容,也不知道在这里更新内容的“正确”方法。我以为我应该留下第一个代码作为参考,买我没有了。 现在我觉得自己在那里。所有步骤均按顺序执行。只是我无法使其工作的第3步(插入N行)。或它插入并停止不进入下一个'.then'或仅插入1行,而我无法想象发生了什么。 在代码中,我用“ BUG 1”和“ BUG 2”两行注释。
使用以下代码:
const sqlite3 = require('sqlite3')
let db = new sqlite3.Database('./test.db');
waitTime = 1
process.stdout.write('Starting...\n')
var test = new Promise((resolve, reject) => {
process.stdout.write('Drop Table... ');
db.run(`DROP TABLE IF EXISTS test`, (err) => {
if (err) {
process.stdout.write(`Dropping Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(`Dropped!\n`)
resolve()
}, waitTime)
}
})
})
test.then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Create Table... ')
db.run(`CREATE TABLE IF NOT EXISTS test (data TEXT)`, (err) => {
if (err) {
process.stdout.write(`Creating Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(`Created!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Insert Line... ')
lines = 10
let loop = (async () => {
for (let i = 0; i < lines; i++) {
await new Promise(resolve =>
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
throw (err)
} else {
setTimeout(() => {
// process.stdout.write(`Line ${i} Inserted!\n`)
process.stdout.write(`, ${i+1}`)
resolve() // BUG 1: if this line is commented, comment it, it will insert only 1 line
}, waitTime)
}
})
)
}
})()
process.stdout.write(`, IDone\n`)
resolve() // BUG 2: If this line is commented, the promise chain stops here
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Count Line(s)... ')
db.all(`SELECT COUNT(*) AS totalLines FROM test`, [], (err, rows) => {
if (err) {
process.stdout.write(`Count Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(` ${rows[0].totalLines} Count!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Select Line(s)... ')
db.all('SELECT data FROM test', [], (err, rows) => {
if (err) {
process.stdout.write(`Select Error ${err.message}\n`)
reject()
} else {
rows.forEach((row) => {
console.log(row.data);
})
setTimeout(() => {
process.stdout.write(`${rows[0].totalLines} Select!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
process.stdout.write('Close DB... ')
db.close((err) => {
if (err) {
process.stdout.write(`Closing Error ${err.message}\n`)
reject()
} else {
setTimeout(() => {
process.stdout.write(`Closed!\n`)
resolve()
}, waitTime)
}
})
})
}).then(() => {
console.log('Finished')
})
经过@CertainPerformance的出色解释(非常感谢),我得以使其运行。我相信这是现在的“正确”方法。也许有一些更好的方法,但是现在,对我来说还可以,下面是最终代码:
const sqlite3 = require('sqlite3')
let db = new sqlite3.Database('./test.db');
lines = 10
process.stdout.write('Starting... ')
var test = new Promise((resolve, reject) => { process.stdout.write(`Promise Created...!\n`)
resolve()
})
test.then(() => { process.stdout.write('Drop Table... ')
return new Promise((resolve, reject) => {
db.run(`DROP TABLE IF EXISTS test`, (err) => {
if (err) {
reject(err)
} else { process.stdout.write(`Dropped!\n`)
resolve() }
})
})
}).then(() => { process.stdout.write('Create Table... ')
return new Promise((resolve, reject) => {
db.run(`CREATE TABLE IF NOT EXISTS test (data TEXT)`, (err) => {
if (err) {
reject(err)
} else {
process.stdout.write(`Created!\n`)
resolve() }
})
})
}).then(() => { process.stdout.write('Insert Line... ')
let insertLoop = (async () => {
for (let i = 0; i < lines; i++) {
await new Promise(resolve =>
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
reject(err)
} else { ( i == 0 ) ? process.stdout.write(`${i + 1}`) : process.stdout.write(`, ${i + 1}`)
resolve() }
})
)
}
process.stdout.write(`, Inserted!\n`)
})()
return insertLoop
}).then(() => { process.stdout.write('Count Line(s)... ')
return new Promise((resolve, reject) => {
db.all(`SELECT COUNT(*) AS totalLines FROM test`, [], (err, rows) => {
if (err) {
reject(err)
} else { process.stdout.write(` ${rows[0].totalLines} Counted!\n`)
resolve()
}
})
})
}).then(() => { process.stdout.write('Close DB... ')
return new Promise((resolve, reject) => {
db.close((err) => {
if (err) {
reject(err)
} else { process.stdout.write(`Closed!\n`)
resolve()
}
})
})
}).then(() => {
console.log('Finished')
}).catch((err) => {
process.stdout.write(`The process did not finish successfully: ${err}`)
})
答案 0 :(得分:1)
有两个主要问题。首先,在第二个.then
中,将loop
声明为立即被调用的async
函数:这意味着loop
将解析为Promise
。修剪后的代码如下:
}).then(() => {
return new Promise((resolve, reject) => {
let loop = (async () => {
// do some asynchronus stuff
})()
resolve() // BUG 2
})
}).then(() => {
仅声明Promise
不会导致当前线程等待它。上面的代码无法正常工作,原因与该代码立即打印after
的原因相同:
console.log('start');
const prom = new Promise((resolve) => {
setTimeout(resolve, 500);
});
console.log('after');
您必须在.then
(或Promise
await
)上调用Promise
,以便在Promise
完成后安排其他操作。或者,如果您当前在.then
内,则可以返回 Promise
,这意味着下一个.then
将在返回后尽快运行Promise
解决:
}).then(() => {
let loop = (async () => {
// do some asynchronus stuff
})();
return loop;
}).then(() => {
// this block will run once `loop` resolves
请注意,上面缺少new Promise((resolve...
构造函数-在.then
内部,仅return
插入下一个Promise
通常是首选方法,因为这意味着更少的代码和avoids an antipattern。
当前代码的另一个问题是不会捕获错误。例如,如果您的
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
throw (err)
// else call resolve()
引发错误,此时await
的承诺将永远无法解决,也不会拒绝-它将永远处于待处理状态且永远无法实现。您应该将reject
作为第二个参数传递给Promise
构造函数,并在出现错误(而不是throw
)时调用它,例如:
await new Promise((resolve, reject) => {
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
reject(err)
} else {
// ...
这样,await
的承诺将被拒绝,这意味着整个loop
将被拒绝,并且如果返回loop
,它将允许{{1 }}来捕获错误,例如:
.catch
请注意,除非每个var test = new Promise((resolve, reject) => {
// ...
});
test.then(() => {
return new Promise(...
// ...
})
.then(() => {
return new Promise(...
// ..
})
.then(() => {
return new Promise(...
// ..
})
.catch((err) => {
process.stdout.write(`The process did not finish successfully:`, err)
// handle errors
});
函数调用都需要顺序执行,否则最好一次发出所有请求,并在每个请求完成后立即解决-这样可以大大减少脚本所需的时间跑步。为每个异步调用创建一个db.
数组,然后在该数组上调用Promises
以得到一个Promise,该Promise将在所有这些Promise.all
完成时解决(或者,在Promises个拒绝中的一个> 。例如,对于第二个Promises
:
.then
幸运的是,代码中没有其他东西可以处理异步循环。
您还可以考虑使用Promisify之类的实用程序功能,它将在每次异步调用时将基于回调的函数转换为Promises,而无需所有额外的}).then(() => {
process.stdout.write('Insert Line... ')
const proms = Array.from(
{ length: lines },
(_, i) => new Promise((resolve, reject) => {
db.run(`INSERT INTO test (data) VALUES ('a')`, (err) => {
if (err) {
process.stdout.write(`Inserting Error ${err.message}\n`)
reject(err)
} else {
setTimeout(() => {
// process.stdout.write(`Line ${i} Inserted!\n`)
process.stdout.write(`, ${i+1}`)
resolve()
}, waitTime);
}
});
})
);
return Promise.all(proms);
}).then(() => {
样板。
答案 1 :(得分:0)
promisifying db
可以对可重用的功能进行进一步的改进,并利用async
/ await
的全部功能而不是mixing it with then
:
const sqlite3 = require('sqlite3')
let db = new sqlite3.Database('./test.db');
function runDbAsync(sql) {
return new Promise((resolve, reject) => {
db.run(sql, (err) => {
if (err) reject(err);
else resolve();
});
});
}
function getDbAsync(sql, val) {
return new Promise((resolve, reject) => {
db.all(`SELECT COUNT(*) AS totalLines FROM test`, [], (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
function closeDbAsync() {
return new Promise((resolve, reject) => {
db.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
function write(text) {
process.stdout.write(text);
}
function writeLn(text) {
write(text + "\n");
}
async function main() {
const lines = 10
writeLn('Starting... ')
write('Drop Table... ');
await runDbAsync(`DROP TABLE IF EXISTS test`);
writeLn(`Dropped!`);
write('Create Table... ');
await runDbAsync(`CREATE TABLE IF NOT EXISTS test (data TEXT)`);
writeLn(`Created!`);
write('Insert Line... ');
for (let i = 0; i < lines; i++) {
await runDbAsync(`INSERT INTO test (data) VALUES ('a')`);
write( i == 0 `${i + 1}` : `, ${i + 1}`);
}
writeLn(`, Inserted!`);
write('Count Line(s)... ')
const rows = getDbAsync(`SELECT COUNT(*) AS totalLines FROM test`, []);
writeLn(` ${rows[0].totalLines} Counted!`)
write('Close DB... ');
await closeDbAsync();
writeLn(`Closed!`);
}
main().then(() => {
console.log('Finished')
}, err => {
writeLn(`The process did not finish successfully: ${err}`)
});