Есть два основных вопроса.Во-первых, во втором .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
часто является предпочтительным способом, так как это означает, что кода намного меньше и избегает использования антипаттерна .
Другая проблема с текущим кодом заключается в том, что ошибки не будут обнаружены.Например, если ваш
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
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, который разрешается после выполнения всех этих Promises
(или отклоняет, как только one изте 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(...
каждый раз, когда происходит асинхронный вызов.