Javascript 为什么Firebase会在once()函数外丢失引用?
我正在使用Firebase和angularJS获取用户列表。我可以使用Javascript 为什么Firebase会在once()函数外丢失引用?,javascript,angularjs,firebase,firebase-realtime-database,Javascript,Angularjs,Firebase,Firebase Realtime Database,我正在使用Firebase和angularJS获取用户列表。我可以使用once()函数读取数据库中的所有用户,但我不明白为什么userList会在下面返回undefined .service('userService', [function() { this.getUsers = function() { var users; var userList; var ref = firebase.database().ref('/users/'
once()
函数读取数据库中的所有用户,但我不明白为什么userList
会在下面返回undefined
.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
,但运气不好
在这里,我遗漏了一些关于承诺/回访的重要信息,我在文档中找不到这些信息;有人能帮我理解我的问题吗?你需要异步思考:
.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=函数(){
var用户;
var ref=firebase.database().ref('/users/');
参考一次(“值”,功能(快照){
users=snapshot.val();
for(var密钥输入用户){
用户[key].id=key;
//做些别的事情
}
console.log(用户);//输出所有用户
},函数(错误){
警报(“错误:”+错误);
});
console.log(用户);//输出“未定义”
}
}]);
我打赌如果你这么做,你会没事的:
.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 );
}
}]);
.service('userService',[function()){
this.getUsers=函数(){
var用户;
var ref=firebase.database().ref('/users/');
参考一次(“值”,功能(快照){
users=snapshot.val();
for(var密钥输入用户){
用户[key].id=key;
//做些别的事情
}
console.log(用户);//输出所有用户
},函数(错误){
警报(“错误:”+错误);
});
setTimeout(函数(){
console.log(用户);//输出“未定义”
}, 1500 );
}
}]);
根据Franks的评论进行澄清:
我应该进一步澄清,setTimeout
只会证明这是一个计时问题。如果你在应用程序中使用setTimeout,你可能会有一段不好的时间,因为你不能可靠地等待n毫秒的结果,你需要获得结果,然后在获得数据快照后启动回调或解决承诺。问题是(正如imjared所说)数据是从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");
}
}]);
其输出将为:
在附加侦听器之前
附加侦听器后
获得价值
知道这个执行的顺序应该可以很好地解释为什么在附加了侦听器之后不能打印用户列表。如果不是,我建议你也读一下这个很棒的答案:
现在来看解决方案:您要么需要在回调中使用用户列表,要么返回承诺
使用回调中的用户列表
这是处理异步代码最古老的方法:将所有需要用户列表的代码移到回调中
ref.once('value', function(snapshot) {
users = snapshot.val();
for(var key in users) {
users[key].id = key;
}
console.log(users); // outputs all 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
});
但是使用承诺比直接使用回调有一个巨大的优势:您可以返回承诺
还愿
通常,您不希望将所有需要用户的代码都放入getUsers()
函数中。在这种情况下,您可以将回调传递到getUsers()
(我在这里不显示,但它非常类似于您可以传递到once()
)的回调,也可以从getUsers()
返回承诺:
有了这项服务,我们现在可以调用getUsers()
,并使用得到的承诺在用户加载后获取:
userService.getUsers().then(function(userList) {
console.log(userList);
})
这样你就驯服了这只异步野兽。好。。。。至少现在是这样。即使是经验丰富的JavaScript开发人员偶尔也会感到困惑,所以如果需要一些时间来适应,也不要担心。谢谢@imjared,我加入了一些额外的逻辑,以确保在我的代码中的异步函数成功完成后填充
userList
,setTimeout()
可以工作,也可以不工作(主要取决于您的网络延迟),但这并不是解决此问题的正确解决方案。正确的解决方案是将需要用户
列表的代码移到回调/then块中。
userService.getUsers().then(function(userList) {
console.log(userList);
})