我使用Firebase和angularJS来获取用户列表。我可以使用once()
函数从我的数据库中读取所有用户,但我无法弄清楚为什么userList
返回未定义的
.service('userService', [function() {
this.getUsers = function() {
var users;
var userList;
var ref = firebase.database().ref('/users/');
ref.once('value').then(function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
// do some other stuff
}
console.log(users); // outputs all users
}).then(function(){
userList = users;
console.log(userList); // outputs all users
},(function(error){
alert('error: ' + error);
});
console.log(userList); // outputs 'undefined'
}
}]);
我等待分配我的userList
变量,直到我完成处理users
,但没有运气。
这里有一些重要的东西,我想念Promises / callbacks,我在文档中找不到它;有人可以帮我理解我的问题吗?
答案 0 :(得分:4)
问题是(正如我所说的那样)数据是从Firebase异步读取的。因此代码不会按照您的想法执行。最简单的方法是通过简单的日志语句简化它:
.service('userService', [function() {
this.getUsers = function() {
var ref = firebase.database().ref('/users/');
console.log("before attaching listener");
ref.once('value').then(function(snapshot) {
console.log("got value");
});
console.log("after attaching listener");
}
}]);
输出结果如下:
附加监听器之前附加监听器后
得到了值
知道执行的顺序应该完全解释为什么在刚刚连接监听器之后无法打印用户列表。如果没有,我建议您也阅读这个好的答案:How do I return the response from an asynchronous call?
现在找到解决方案: 需要使用回调中的用户列表或返回承诺。
这是处理异步代码的最古老的方法:将需要用户列表的所有代码移动到回调中。
ref.once('value', function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
}
console.log(users); // outputs all users
})
您正在重新编写代码,首先加载用户列表,然后打印其内容" to"每当加载用户列表时,打印其内容"。定义上的差异很小,但突然之间你完全有能力处理异步加载。
您也可以使用承诺执行相同操作,就像在代码中一样:
ref.once('value').then(function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
// do some other stuff
}
console.log(users); // outputs all users
});
但是使用承诺比直接使用回调有一个巨大的优势:你可以返回承诺。
通常,您不希望将需要用户的所有代码放入getUsers()
函数中。在这种情况下,你可以 将回调传递给getUsers()
(我不会在这里显示,但它与你可以传递给{{的回调非常相似) 1}})或您可以从once()
返回承诺:
getUsers()
使用此服务,我们现在可以调用this.getUsers = function() {
var ref = firebase.database().ref('/users/');
return ref.once('value').then(function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
// do some other stuff
}
return(users);
}).catch(function(error){
alert('error: ' + error);
});
}
并使用生成的承诺在用户重新加载后获取用户:
getUsers()
然后你已经驯服了异步野兽。嗯......现在至少。即使是经验丰富的JavaScript开发人员也会偶尔混淆,所以如果需要一段时间习惯,请不要担心。
答案 1 :(得分:1)
你需要考虑异步:
.service('userService', [function() {
this.getUsers = function() {
var users;
var ref = firebase.database().ref('/users/');
<!-- this happens ASYNC -->
ref.once('value', function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
// do some other stuff
}
console.log(users); // outputs all users
},function(error){
alert('error: ' + error);
});
<!-- end async action -->
console.log(users); // outputs 'undefined'
}
}]);
我敢打赌,如果你这样做,你会没事的:
.service('userService', [function() {
this.getUsers = function() {
var users;
var ref = firebase.database().ref('/users/');
<!-- this happens ASYNC -->
ref.once('value', function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
// do some other stuff
}
console.log(users); // outputs all users
},function(error){
alert('error: ' + error);
});
<!-- end async action -->
window.setTimeout( function() {
console.log(users); // outputs 'undefined'
}, 1500 );
}
}]);
根据弗兰克斯的评论澄清:
我应该进一步澄清setTimeout
只会证明这是一个时间问题。如果你在你的应用程序中使用setTimeout,你可能会遇到一个糟糕的时间,因为你无法可靠地等待n毫秒的结果,你需要得到它们然后在获得快照之后触发回调或解决一个承诺数据。