获取Promise之外的变量值

时间:2016-04-12 13:19:45

标签: javascript angularjs angular-promise

我在AngularJs中有一个服务,它将从Database返回一个值。

/.../

当我在我的控制器中注入此服务时,它将返回一个Promise, 但我需要这个承诺价值在我的功能之外,我该怎么做?

Plunkr link of code

3 个答案:

答案 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