我在AngularJs中有一个服务,它将从Database返回一个值。
/.../
当我在我的控制器中注入此服务时,它将返回一个Promise, 但我需要这个承诺价值在我的功能之外,我该怎么做?
答案 0 :(得分:3)
从您的plunker代码中您可以获得如下服务:
angular.module('plunker');
.service('myService', function($firebaseRef, $firebaseObject){
this.getUserData = function(el) {
$firebaseObject($firebaseRef.users.child(this.localStorage().uid)).$loaded(function(data) {
el(data);
})
}
});
和这样的控制器:
app.controller('MainCtrl', function($scope, myService) {
myService.getUserData(function(response) {
$scope.uid = response;
})
console.log($scope.uid);
$scope.postRequest = function(val) {
$firebaseArray($firebaseRef.requests.child($scope.uid)).$add(val);
console.log(val)
console.log($scope.request);
}
});
问题是,console.log($scope.uid);
行打印undefined
。
您正在考虑标准阻止编程的术语,但在这种情况下,对getUserData
的调用是非阻塞的,这意味着您不会等待response
,而是您只需将请求发送到服务器(Firebase)并继续下一个console.log
语句。
当客户端读取服务器返回的成功响应(HTTP 2xx)时,将调用回调function(response) { $scope.uid = response; }
。这至少需要时间请求前往服务器以及返回的响应+服务器实际获取数据所需的时间。例如150ms。
因此,基本上在执行console.log
语句时,仍未调用response
回调,即。 $scope.uid
未设置,这意味着console.log
将打印undefined
。
要解决此问题,您需要在回调本身中执行代码,该代码取决于服务器的响应。例如:
app.controller('MainCtrl', function($scope, myService) {
myService.getUserData(function(response) {
$scope.uid = response;
console.log($scope.uid);
// and any other code which depends on the $scope.uid
});
// ...
});
很酷的因素是通过$q
服务使用AngularJS承诺。例如,您可以像这样重新定义您的服务:
angular.module('plunker');
.service('myService', function($q, $firebaseRef, $firebaseObject){
var deferred = $q.defer();
this.getUserData = function(el) {
$firebaseObject($firebaseRef.users.child(this.localStorage().uid)).$loaded(function(data) {
deferred.resolve(data);
});
};
return deferred.promise;
});
然后在你的控制器中你可以使用你的服务方法:
app.controller('MainCtrl', function($scope, myService) {
myService.getUserData()
.then(function(data) {
$scope.uid = data;
console.log($scope.uid);
// and any other code
// you can also return promises here and then chain
// them, read about AngularJS promises
});
// ...
});
这与前面的例子基本相同,但是通过避免回调地狱可以获得更好的可读性。
我注意到您使用了postRequest
$scope.uid
函数。如果你没有$scope.uid
,我想你不想执行这个功能。我还猜测某个事件会调用此函数,例如单击按钮。我的建议是在加载$scope.uid
之前禁用该按钮或其他调用此函数的内容。
例如:
<button type="button" ng-click="postRequest(something)" ng-disabled="uid === undefined">Post</button>
希望这有帮助。
答案 1 :(得分:1)
您所讨论的问题与您在承诺返回任何内容之前尝试使用$scope.uid
这一事实有关。
你可以通过几个步骤来解决这个问题,主要是你可以在使用之前初始化范围var。例如,如果response
是一个对象,你可以这样做:
$scope.uid = {};
userData.getUserData(function(response) {
$scope.uid = response;
});
然后你的var将不会被定义。但是你也应该考虑你何时以及如何使用这个变量,如果你想这样做或不这样做,那将会有效。
如果您这样记录,它将起作用
userData.getUserData(function(response) {
$scope.uid = response;
console.log($scope.uid);
});
如果您这样记录,将不会工作,因为此日志不会等待您承诺在记录之前返回;
userData.getUserData(function(response) {
$scope.uid = response;
});
console.log($scope.uid);
您需要提供更多信息,以确定如何最好地处理使用此返回的信息和本地变量。但问题的一般概念是你试图在承诺回来之前记录变量。
TL:DR 您可以访问函数外部的$ scope.uid,您需要等待响应才能将数据提供给内部,您可以初始化如果您不希望它以未定义的方式开始
更新:您需要使用回叫来拨打第二个电话第一次回电后
userData.getUserData(function(response) {
$scope.postRequest(response);
});
$scope.postRequest = function(val) {
$firebaseArray($firebaseRef.requests.child($scope.uid)).$add(val);
console.log(val) console.log($scope.request);
}
你的插件已修好:https://plnkr.co/edit/KbVoni3jfnHm54M80kYl?p=preview
答案 2 :(得分:-1)
您必须等到从userData.getUserData获取响应的过程完成。
据我所知,有三种方法可以解决这个问题:
使用回拨
function getUserData(callback){
userData.getUserData(function(response) {
callback(response);
});
}
然后你调用那个函数
getUserData(function(response){
$scope.uid = response;
// then you can proceed and use the $scope.uid here
});
将其包裹在功能中
getUserData(function(response){
callAnotherFunction(response);
});
function callAnotherFunction(response){
console.log(response);
// You can use the value inside this function
}
或使用超时
您可以使用$ timeout为请求提供时间并将其分配给$ scope.uid