我正在尝试在 NodeJS 11.6.x 上开发一个相对简单的测试。我并不是真正的开发人员,但有时会尝试编写一些代码。
我的目标是创建一个 SQLite 数据库并在每次运行代码时重复一些步骤: 1.删除表(如果存在) 2. 创建表 3.插入N行 4. 统计数据库中有多少行 5.关闭数据库
我首先尝试使用回调的基本方法,但无法找到完成步骤 3(插入 N 行)并寻找解决方案的方法, promise async/await“模式”听起来是完成所有事情的方法。
但是,重构代码后,步骤 1(删除表)没有运行,我仍然无法执行步骤 3(插入 N 行)并且不知道发生了什么。我也尝试过使用 Promise 包,但没有成功。
有人可以看一下并提供帮助吗?如果可能的话,请解释一下或提供一些建议吗?
提前致谢
编辑:嗯,我不习惯在这里发帖,也不知道在这里更新内容的“正确”方法。我相信我应该留下第一个代码作为引用,购买我不再有。 现在我想我已经快到了。所有步骤都按顺序执行。只是第 3 步(插入 N 行)我无法使其工作。或者它插入并停止而不进入下一个“.then”,或者它只插入 1 行,我无法想象发生了什么。 在代码中,我用“BUG 1:”和“BUG 2:”注释了两行。
- 如果我俩都被评论,我就会明白发生了什么,它只插入 1 行并且不继续 promise 链
- 如果我评论 BUG 1 并让 BUG 2 处于事件状态,它只会插入一行并继续。我想我明白为什么
- 如果我评论 BUG 2 并让 BUG 1 处于事件状态,它会插入所有行,但不会再次继续,我想我明白为什么
- 如果我取消注释两者(我认为应该有效的方式。不起作用,并返回附加错误“段错误”
下面的代码:
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}`)
})
最佳答案
有两个主要问题。首先,在第二个 .then
中,将 loop
声明为立即调用的 async
函数:这意味着 loop
将解析为 Promise
。修剪后的代码如下所示:
}).then(() => {
return new Promise((resolve, reject) => {
let loop = (async () => {
// do some asynchronus stuff
})()
resolve() // BUG 2
})
}).then(() => {
单独声明一个Promise
不会导致当前线程等待它。上面的代码无法按预期工作,原因与此代码在 after
immediately 后打印的原因相同:
console.log('start');
const prom = new Promise((resolve) => {
setTimeout(resolve, 500);
});
console.log('after');
您必须对 Promise
调用 .then
(或 await
Promise
),以便在 Promise
完成后安排其他操作。或者,如果您当前位于 .then
中,则可以返回 Promise
,这意味着一旦返回的 Promise
解析,下一个 .then
就会运行:
}).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()
抛出一个错误,当前正在等待的 Promise 将永远不会解析,也不会拒绝 - 它将永远保持挂起和未实现的状态。您应该将 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
ed Promise 将被拒绝,这意味着整个 loop
将被拒绝,如果 loop
返回,它将允许 .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.all
来获取一个 Promise,该 Promise 在所有这些 Promises
完成时解析(或者,一旦这些 Promises
中的一个拒绝就拒绝)。例如,对于第二个 .then
:
}).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(() => {
幸运的是,您的代码中没有其他内容可以处理异步循环。
您还可以考虑像 Promisify 这样的实用函数,它将基于回调的函数转换为 Promises,而无需每次异步调用时使用所有额外的 new Promise(...
样板文件。
关于node.js - 完全迷失了 promise async/await,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54661796/