Firebase читает данные асинхронно, чтобы приложение не блокировалось во время ожидания сетевого трафика. Затем, когда данные загружены, он вызывает функцию обратного вызова.
Это легко увидеть, разместив несколько операторов журнала:
console.log("Before starting to load data");
ref.once("value", function(snapshot) {
console.log("Got data");
});
console.log("After starting to load data");
Когда вы запускаете этот код, вывод:
Перед началом загрузки данных
После начала загрузки данных
Получил данные
Вероятно, это не тот порядок, который вы ожидали, но он точно объясняет, почему вы получаете массив нулевой длины при его регистрации. Но поскольку основной код продолжал работать сразу, к тому времени, когда вы console.log(locations, locations.length)
данные еще не загрузили, и вы еще не отправили их в массив.
Решение состоит в том, чтобы гарантировать, что весь код, которому нужны данные из этих данных, либо находится внутри обратного вызова, либо вызывается оттуда.
Так что это будет работать:
var locations = []; // Empty array
var ref = db.ref("locations/");
ref.once("value", function(snapshot) {
snapshot.forEach(function(item) {
var itemVal = item.val();
locations.push(itemVal);
});
console.log(locations, locations.length);
});
Как будет это:
function loadLocations(callback) {
var locations = []; // Empty array
var ref = db.ref("locations/");
ref.once("value", function(snapshot) {
snapshot.forEach(function(item) {
var itemVal = item.val();
locations.push(itemVal);
});
callback(locations);
});
});
loadLocations(function(locations) {
console.log(locations.length);
});
Более современный вариант этого последнего фрагмента - возвращать обещание, а не передавать обратный вызов.
function loadLocations() {
return new Promise(function(resolve, reject) {
var locations = []; // Empty array
var ref = db.ref("locations/");
ref.once("value", function(snapshot) {
snapshot.forEach(function(item) {
var itemVal = item.val();
locations.push(itemVal);
});
resolve(locations);
});
})
});
И тогда вы можете назвать это так:
loadLocations().then(function(locations) {
console.log(locations.length);
});
Или с современным JavaScript вы можете использовать async
/ await
и делать:
let locations = await loadLocations()
console.log(locations.length);
Просто имейте в виду, что этот последний фрагмент все еще имеет то же асинхронное поведение, а среда выполнения JavaScript (или транспортер) просто скрывает его от вас.