finally(), выполненный до этого (), завершается при выполнении подготовленной инструкции

#node.js #sql-server #asynchronous #promise #es6-promise

#node.js #sql-сервер #асинхронный #обещание #es6-обещание

Вопрос:

Я новичок в node.js и у меня возникла проблема с подключением к mssql базе данных и подготовкой / выполнением подготовленной инструкции.

У меня есть следующий код:

 this.connectionPool.connect().then(pool => {
    // Create prepared statement
    stmt = new mssql.PreparedStatement(pool);

    // PREPARE
    stmt.prepare(command, err => {
        //TODO: check for errors in prepare
                
        // EXECUTE
        stmt.execute((err, result) => {
            //TODO: check for errors in execute

            // UNPREPARE
            stmt.unprepare(err => {
                //TODO: check for errors in unprepare
            });

            console.log(`Rows affected: ${stmt.rowsAffected}`);
            console.log(`Result: ${result}`);
            return resu<
        });
    });
}).catch(err => {
    console.log(`Connection pool error: ${err}`);
}).finally(() => {
    // Close connection
    console.log("Closing connection.");
    if (this.connectionPool.connected) {
        this.connectionPool.close();
    }
});
  

Я обнаружил, что finally() обещание выполняется одновременно с then() , чего я бы не ожидал. Это приводит к закрытию соединения до того, как оператор может быть подготовлен.

 ConnectionError: connection is closed
  

Как я могу гарантировать, что соединение будет закрыто только после выполнения инструкции?

Ответ №1:

Обратные вызовы выполняются асинхронно, поэтому вам нужно добавить результат execute в цепочку обещаний:

 this.connectionPool.connect().then(pool => {
  // Create prepared statement
  stmt = new mssql.PreparedStatement(pool)

  // return a promise to add it to the promise chain
  return new Promise((resolve, reject) => {
    stmt.prepare(command, err => {
      // TODO: check for errors in prepare
      if (err) {
        reject(err)
        return
      }
      // EXECUTE
      stmt.execute((err, result) => {
        // TODO: check for errors in execute
        if (err) {
          reject(err)
          return
        }

        // UNPREPARE
        stmt.unprepare(err => {
          // TODO: check for errors in unprepare
          if (err) {
            reject(err)
          }
        })

        console.log(`Rows affected: ${stmt.rowsAffected}`)
        console.log(`Result: ${result}`)
        resolve(result)
      })
    })
  })
}).catch(err => {
  console.log(`Connection pool error: ${err}`)
}).finally(() => {
  // Close connection
  console.log('Closing connection.')
  if (this.connectionPool.connected) {
    this.connectionPool.close()
  }
})
  

Если stmt.prepare обещание поддержки готово из коробки, вы можете вернуть его, не оборачивая в new Promise


Все обещанные версии:

 this.connectionPool.connect().then(pool => {
  // Create prepared statement
  stmt = new mssql.PreparedStatement(pool)
  return stmt.prepare(command)
    .then(() => stmt.execute())
    .then((result) => {
      console.log(`Rows affected: ${stmt.rowsAffected}`)
      console.log(`Result: ${result}`)
    })
    .finally(() => stmt.unprepare())
}).catch(err => {
  console.log(`Connection pool error: ${err}`)
}).finally(() => {
  // Close connection
  console.log('Closing connection.')
  if (this.connectionPool.connected) {
    this.connectionPool.close()
  }
})
  

Комментарии:

1. Я считаю, что stmt.prepare , stmt.execute и stmt.unprepare возвращает Promise , если обратный вызов опущен. Не могли бы вы показать мне, как это можно использовать вместо дополнительной new Promise оболочки?

2. С помощью этого примера я обнаружил, что теперь ни методы catch() or finally() не выполняются, что означает, что соединение никогда не закрывается.

3. Обновленный ответ. Если этот блок не выполняется, проверьте последний, который выполняется. Я думаю, что проблема могла возникнуть до вызова этого фрагмента кода

4. В вашем втором примере я нахожу, что stmt.unprepare() из finally() метода выполняется раньше stmt.prepare() , и я получаю ошибку PreparedStatementError: statement is not prepared. Call prepare() first. . Это в основном то же самое, что я получал ранее, когда finally() метод выполнялся первым и закрывал соединение. Почему finally() не дожидается завершения then() ?

5. Не могли бы вы проверить, что возвращает метод prepare? Я думаю, что это не обещание, по этой причине оно не ожидается

Ответ №2:

Все методы .prepare() , .execute() и .unprepare() принимают обратные вызовы. Если эти методы не «обещаны», this.connectionPool.connect().then().catch().finally() цепочка не имеет средств для получения информации о результатах процесса подготовки-выполнения-неподготовленности.

Процесс подготовки-выполнения-неподготовленности может быть обещан следующим образом:

 this.connectionPool.connect()
.then(pool => {
    let stmt = new mssql.PreparedStatement(pool);
    return new Promise((resolve, reject) => {
        stmt.prepare(command, err => {
            if(err) {
                reject(err);
            } else {
                stmt.execute((err, result) => {
                    if(err) {
                        reject(err);
                    } else {
                        stmt.unprepare(err => {
                            if(err) {
                                reject(err);
                            } else {
                                resolve(result);
                            }
                        });
                    }
                });
            }
        });
    });
})
.catch(err => {
    console.log(`Prepared statement error: ${err.message}`);
})
.finally(() => {
    if (this.connectionPool.connected) {
        this.connectionPool.close();
    }
});
  

Теперь блоки catch() и finally() будут упорядочены так, как вы ожидаете.

Комментарии:

1. Если .prepare() (и другие методы) возвращают Promise, как вы сказали выше, тогда все становится намного проще.