Мне нужна помощь, чтобы понять причину, по которой после forEach моя константа становится пустой. Я читаю и нахожу несколько вопросов, но ни один из них не помогает мне понять. Я считаю, что это происходит потому, что JS асинхронен, но я не могу понять, как решить эту проблему.
Итак, код очень прост. У меня есть API NodeJS, который подключается к нескольким базам данных и возвращает всю информацию. Я использую pg-prom для подключения к PostgreSQL.
export default class AllInfo {
constructor(databases) {
this.databases = databases;
this.options = {
promiseLib: promise,
};
this.databaseConnection = new Pg(this.options);
}
И после этого метод трюка:
getAllInformation() {
const entidades = [];
this.databases.getStringConnection().forEach((db) => {
const connection = this.databaseConnection(db);
connection.any('SELECT * FROM information').then((data) => {
entidades.push(data);
});
connection.$pool.end();
});
return entidades;
}
В этом коде мой возврат всегда пустой ([]), когда его запрашивают.
Если я регистрирую константы внутри цикла, информация регистрируется успешно. Но если я войду после цикла и до возврата, он будет пустым.
getAllInformation() {
const entidades = [];
this.databases.getStringConnection().forEach((db) => {
const connection = this.databaseConnection(db);
connection.any('SELECT * FROM information').then((data) => {
entidades.push(data);
console.info(entidades) // here it works
});
connection.$pool.end();
});
return entidades;
}
И если я попытаюсь выйти наружу:
getAllInformation() {
const entidades = [];
this.databases.getStringConnection().forEach((db) => {
const connection = this.databaseConnection(db);
connection.any('SELECT * FROM information').then((data) => {
entidades.push(data);
});
connection.$pool.end();
});
console.info(entidades) // here doesn't work
return entidades;
}
Кто-нибудь может объяснить, почему это происходит и где я ищу решение?
Зачем вам const
? Следует ли менять const
? Может, хочешь var
?
«Использование forEach для создания другого массива с помощью метода push…» в любом случае полностью неверен. Для этого был создан .map
.
Поскольку вы уже используете обещания, взгляните на Promise.all
.
@Bunyk const
не меняет свой ценить, но для объектов (а массив является объектом) это означает переназначение объекту другой. Например, const myObj = {name: "alice" };
, за которым следует myObj = {name: "bob"}
. Однако myObj.name = "bob"
действителен. Кроме того, если вам не нужен const
, то лучше использовать let
, а не var
. Есть очень мало причин, по которым вам нужен var
, если вы можете просто использовать ключевые слова ES6.
@Bunyk дополняет объяснение vlaz, если я объявляю let, Lint выдает мне: «'entidades' никогда не переназначается. Вместо этого используйте 'const'. (Prefer-const)».
@vlaz Раньше я не встречал этого вопроса. Возможно, это потому, что упоминается Ajax. Спасибо, что там хорошая информация.
Кроме того, инициализация соединения и последующее уничтожение пула соединений внутри метода выглядят очень неправильно. Это не то, как вы используете pg-promise
. См .: Где инициализировать pg-prom.
Привет, @ vitaly-t, это большая честь. Я использую для подключения к нескольким базам данных и выполнения выбора. Поэтому, если я закрываю соединение, я получаю предупреждение в консоли. Также это соединение больше нигде не используется. Представьте себе многопользовательское приложение, в котором вы хотите выполнять запросы к разным базам данных. Я не знаю, как сохранить соединение открытым для следующего запроса, если я не знаю, какая база данных идет. Вы так использовали? Есть предположения?
@Francisco Simple - вы создаете по одному db
для каждого соединения, а затем повторно используете их. И если у вас есть динамический список, должен быть уникальный ключ, который вы можете поместить в хэш, а затем при необходимости вытащить оттуда нужный объект db
.
@ vitaly-t, спасибо, я сделаю это. Цените помощь.
connection.any()
возвращает обещание и выполняет анонимную функцию, которая помещает данные в ваш массив ПОСЛЕ выполнения обещания. Вот почему анонимная функция выполняется асинхронно. Однако вы можете подождать, пока данные будут возвращены функцией any, например:
let data = await connection.any('SELECT * FROM information');
entidades.push(data);
Учитывая, что OP все еще использует forEach
, размещение await
там не поможет - это эквивалентно вызову .then()
, который они в настоящее время используют.
Привет, @Psycho, спасибо за попытку помочь. Если я изменю этот код, проект не будет компилироваться. Ошибка: «ожидание - зарезервированное слово».
Это так, как вы думаете. Он возвращает пустой массив, потому что JS является асинхронным, а вы возвращаете данные, как если бы они были синхронными.
Вы можете поместить обещания connection.any('SELECT * FROM information')
в массив вместо того, чтобы помещать результат, таким образом, вы можете подождать, пока все обещания не будут разрешены / отклонены, чтобы продолжить.
попробуй это:
function getAllInformation() {
const entidades = [];
var entidadesPromises = [];
this.databases.getStringConnection().forEach((db) => {
const connection = this.databaseConnection(db);
entidadesPromises.push(connection.any('SELECT * FROM information'));
connection.$pool.end();
});
return Promise.all(entidadesPromises).then((data) => {
entidades.push(data);
console.info(entidades) // here it works
return entidades;
});
}
getAllInformation().then(entidades => {
// Entidades will be an array containing the data retrieved from the databases.
});
Большое тебе спасибо. Прекрасно работает. Теперь я понимаю. Я прочитаю больше о обещаниях и асинхронном режиме JS.
Я рад, что помог. Если позволите, дам хорошую книгу. Погуглите «вы не знаете js-обещаний».
Я буду читать, я ценю совет. Спасибо.
Я также думаю, что это асинхронная проблема, попробуйте использовать обещание или async / await