У меня есть база данных, разработанная (примерно) следующим образом:
"users": {
"userId1": {
"challenges" {
"challengeId1"=true,
"challengeId2"=true
}
},
"userId2": {
"challenges" {
"challengeId1"=true,
"challengeId3"=true
}
}
...
"userIdN": {...}
}
"challenges": {
"challengeId1": {
"title" = "Title 1
}
},
"challengeId2": {
"title" = "Title 2
}
},
"challengeId3": {
"title" = "Title 3
}
}
}
В моем клиенте я делаю следующие вложенные вызовы:
const fetchChallengeList = (currUser) => {
firebase.database().ref(`/users/${currUser.uid}/challenges`).on('child_added', snapshot => {
firebase.database().ref(`/challenges/${snapshot.key}`).on('value', snapshotChall => {
// Dispatch some stuff to UI
});
});
};
Обычно это отлично работает и синхронизирует все. Использование .on гарантирует, что каждый раз, когда добавляется новый вызов (пользователю: вызовы) или изменяется существующий вызов, тогда firebase обновляет пользовательский интерфейс.
Проблема в начальной загрузке. В идеале я хотел бы подождать, чтобы обновить пользовательский интерфейс (например, показать значок загрузки), пока СУЩЕСТВУЮЩИЕ задачи для пользователя изначально не закончат загрузку. В противном случае пользовательский интерфейс будет трястись и расширяться в течение 5-10 секунд. Поскольку все вызовы асинхронны и остаются открытыми, я понятия не имею, когда начальная установка завершила загрузку. Есть ли способ отследить это?
Я знаком с Promise и .then, а также с Promise.all. Но я не уверен, к чему это меня привело. .on ('child_added') остается открытым и возвращает значение для каждой (дочерней) задачи. Вы априори не знаете, сколько его вернут. И даже если вы это сделали, вложенный обратный вызов firebase выполняется асинхронно и в разных областях. Вложенные обратные вызовы не знают друг о друге. Так что я не вижу возможности использовать Promise и .then.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я бы создал способ показать / скрыть загрузчик что-то вроде
toggleLoader(display) => {
// dont know the framework you are using
if (display) {
$('.loader').show();
} else {
$('.loader').hide();
}
}
Затем вы можете вызвать этот метод после загрузки задач.
const fetchChallengeList = (currUser) => {
firebase.database().ref(`/users/${currUser.uid}/challenges`).on('child_added', snapshot => {
firebase.database().ref(`/challenges/${snapshot.key}`).on('value', snapshotChall => {
// Dispatch some stuff to UI
toggleLoader(false);
});
});
};
Я бы также добавил некоторую обработку ошибок, чтобы выключить загрузчик в случае ошибки.
Вызовы firebase асинхронны. Если имеется более одного запроса, ваш ответ остановит загрузчик после получения первого запроса.
Я понимаю, что это асинхронно, поэтому почему код находится в обратном вызове? firebase.googleblog.com/2016/01/…
Может я что-то упускаю. Но предположим, что у меня есть 3 проблемы. Это означает, что вложенный ref.on будет вызываться 3 отдельных раза, а затем обратный вызов, содержащий toggleLoader, будет вызываться 3 отдельных раза. Это означает, что при первом вызове пользовательский интерфейс будет отображаться, а 2-й и 3-й обратные вызовы покажут именно то, чего я пытаюсь избежать. (Я запустил код с консольным журналом для подтверждения). Но опять же, возможно, я неправильно понимаю, что вы предлагаете.
Кроме того, я не могу использовать .then, как предполагает отправленная вами статья, потому что я вызываю .on, который, в отличие от .once, не возвращает Promise. Мне нужно использовать .on, чтобы изменения в задачах синхронизировались автоматически.
@ Пол, я понимаю, о чем ты говоришь. Я думаю, что тогда получить желаемый результат непросто. Что вы могли бы сделать, так это получить все child_added из первого обратного вызова с помощью getChildrenCount() и увеличить счетчик на втором. Как только они сравняются, выключите загрузчик. В противном случае я бы предложил использовать fadeIns в вашем пользовательском интерфейсе, чтобы переход не был таким резким.
посмотрите JavaScript
Promiseи.then