Полностью потерян с обещанием асинхронного / ожидания - PullRequest
0 голосов
/ 13 февраля 2019

Я пытаюсь разработать относительно простой тест на NodeJS 11.6.x.Я не являюсь разработчиком, но иногда пытаюсь заняться программированием.

Моя цель - создать базу данных SQLite и повторять несколько шагов при каждом запуске кода: 1. Удалите таблицу, если она существует 2Создайте таблицу 3. Вставьте N строк 4. Подсчитайте, сколько строк в базе данных 5. Закройте базу данных

Сначала я попробовал базовый подход с использованием обратного вызова, но не смог найти способ сделатьшаг 3 (вставьте N строк) и в поисках решения, обещание async / await 'pattern' звучит как способ выполнить все.

Но после рефакторинга кода шаг 1 (отбрасываемая таблица) нене выполняется, и я все еще не могу выполнить шаг 3 (вставить N строк) и понятия не имею, что происходит.Я также попытался использовать пакет обещаний, но безуспешно.

Может ли кто-нибудь взглянуть и помочь по этому вопросу и, если возможно, объяснить и дать какой-нибудь совет?

Заранее спасибо

Отредактировано: Ну, я не привык публиковать здесь в SO и не знаю «правильный» способ обновить вещи здесь.Я полагаю, я должен был оставить первый код в качестве ссылки, купить у меня больше нет.Теперь я думаю, что я почти там.Все шаги выполняются по порядку.Просто шаг 3 (вставьте N строк), что я не могу заставить его работать.Или он вставляет и останавливается, не переходя к следующему «.then», или просто вставляет 1 строку, и я не могу представить, что происходит.В коде я прокомментировал в две строки «BUG 1:» и «BUG 2:».

  1. Если я оба прокомментировал, я понял, что происходит, он вставил только 1 строку ине продолжать цепочку обещаний
  2. Если я прокомментирую BUG 1 и оставлю BUG 2 активным, он вставит только одну строку и продолжит работу.Я думаю, что понимаю, почему
  3. Если я прокомментирую BUG 2 и оставлю BUG 1 активным, он вставит все строки, но не продолжит, и снова, я думаю, я понимаю, почему
  4. Если я раскомментирую оба (я думаю, что это должно работать. Не работает, и возвращает дополнительную ошибку «Ошибка сегментации»

Ниже код:

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}`)
})

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Дальнейшие улучшения могут быть сделаны путем обещания db в функции многократного использования и использования полной мощности async / await вместо , смешивая его с 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}`)
});
0 голосов
/ 14 февраля 2019

Есть два основных вопроса.Во-первых, во втором .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(... каждый раз, когда происходит асинхронный вызов.

...