当前位置: 首页 > 面试题库 >

AngularJS:在哪里使用promise?

龙华翰
2023-03-14
问题内容

我看到了一些使用 Promise 访问FB Graph API 的Facebook登录服务的示例。

范例1

this.api = function(item) {
  var deferred = $q.defer();
  if (item) {
    facebook.FB.api('/' + item, function (result) {
      $rootScope.$apply(function () {
        if (angular.isUndefined(result.error)) {
          deferred.resolve(result);
        } else {
          deferred.reject(result.error);
        }
      });
    });
  }
  return deferred.promise;
}

以及"$scope.$digest() // Manual scope evaluation"获得响应时使用的服务

范例2

angular.module('HomePageModule', []).factory('facebookConnect', function() {
    return new function() {
        this.askFacebookForAuthentication = function(fail, success) {
            FB.login(function(response) {
                if (response.authResponse) {
                    FB.api('/me', success);
                } else {
                    fail('User cancelled login or did not fully authorize.');
                }
            });
        }
    }
});

function ConnectCtrl(facebookConnect, $scope, $resource) {

    $scope.user = {}
    $scope.error = null;

    $scope.registerWithFacebook = function() {
        facebookConnect.askFacebookForAuthentication(
        function(reason) { // fail
            $scope.error = reason;
        }, function(user) { // success
            $scope.user = user
            $scope.$digest() // Manual scope evaluation
        });
    }
}

JSFiddle

问题是:

  • 上面的示例有什么 区别
  • 使用 $ q 服务的 原因案例 是什么? __
  • 以及它如何 运作

问题答案:

这并不是您问题的完整答案,但是希望当您尝试阅读$q服务文档时,这将对您和其他人有所帮助。我花了一段时间才了解它。

让我们暂时搁置AngularJS,只考虑Facebook API调用。当来自Facebook的响应可用时,两个API调用都使用 回调 机制来通知调用者:

  facebook.FB.api('/' + item, function (result) {
    if (result.error) {
      // handle error
    } else {
      // handle success
    }
  });
  // program continues while request is pending
  ...

这是用于处理JavaScript和其他语言中的异步操作的标准模式。

当您需要执行一系列异步操作时,此模式会出现一个大问题,其中每个后续操作都取决于上一个操作的结果。这就是这段代码正在做的事情:

  FB.login(function(response) {
      if (response.authResponse) {
          FB.api('/me', success);
      } else {
          fail('User cancelled login or did not fully authorize.');
      }
  });

它首先尝试登录,然后仅在确认登录成功后才向Graph API发出请求。

即使在仅将两个操作链接在一起的情况下,事情也开始变得混乱。该方法askFacebookForAuthentication接受失败和成功的回调,但是FB.login成功但FB.api失败了怎么办?success无论方法的结果如何,此方法始终会调用回调FB.api

现在想象一下,您正在尝试编写一个健壮的,包含三个或更多个异步操作的序列,以一种能够正确处理每个步骤的错误的方式进行,并且几周后所有人甚至其他人都可以清楚地看到。可能,但是很容易保持嵌套这些回调并一路走错。

现在,让我们暂时搁置Facebook API,只考虑$q服务实现的Angular Promises API
。此服务实现的模式是尝试将异步编程转换为类似于线性简单语句的序列,并具有在任何方式的步骤中“引发”错误并最终处理错误的能力,其语义类似于熟悉的try/catch块。

考虑这个人为的例子。假设我们有两个函数,其中第二个函数使用第一个函数的结果:

 var firstFn = function(param) {
    // do something with param
    return 'firstResult';
 };

 var secondFn = function(param) {
    // do something with param
    return 'secondResult';
 };

 secondFn(firstFn());

现在想象一下firstFn和secondFn都需要很长时间才能完成,因此我们要异步处理此序列。首先,我们创建一个新deferred对象,该对象代表一系列操作:

 var deferred = $q.defer();
 var promise = deferred.promise;

promise属性表示链的最终结果。如果在创建承诺后立即记录了承诺,您会看到它只是一个空对象({})。没什么可看的,继续前进。

到目前为止,我们的承诺仅代表该链中的起点。现在让我们添加两个操作:

 promise = promise.then(firstFn).then(secondFn);

then方法将一条步骤html" target="_blank">添加到链中,然后返回代表扩展链最终结果的新promise。您可以根据需要添加任意多个步骤。

到目前为止,我们已经建立了功能链,但是实际上什么也没有发生。您可以通过调用开始deferred.resolve,指定要传递给链中第一步的初始值:

 deferred.resolve('initial value');

然后…仍然没有任何反应。为了确保正确观察模型更改,Angular直到下一次$apply调用时才真正调用链中的第一步:

 deferred.resolve('initial value');
 $rootScope.$apply();

 // or     
 $rootScope.$apply(function() {
    deferred.resolve('initial value');
 });

那么错误处理呢?到目前为止,我们只在链的每个步骤中指定了一个 成功处理程序
then还接受错误处理程序作为可选的第二个参数。这是承诺链的另一个更长的例子,这次是错误处理:

 var firstFn = function(param) {
    // do something with param
    if (param == 'bad value') {
      return $q.reject('invalid value');
    } else {
      return 'firstResult';
    }
 };

 var secondFn = function(param) {
    // do something with param
    if (param == 'bad value') {
      return $q.reject('invalid value');
    } else {
      return 'secondResult';
    }
 };

 var thirdFn = function(param) {
    // do something with param
    return 'thirdResult';
 };

 var errorFn = function(message) {
   // handle error
 };

 var deferred = $q.defer();
 var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);

如本例所示,链中的每个处理程序都有机会将流量转移到下一个 错误 处理程序,而不是下一个 成功
处理程序。在大多数情况下,您可以在链的末尾拥有单个错误处理程序,但也可以具有尝试恢复的中间错误处理程序。

为了快速返回您的示例(和您的问题),我只想说它们代表了两种不同的方式来使Facebook的面向回调的API适应Angular的观察模型变化的方式。第一个示例将API调用包装在一个promise中,可以将其添加到范围中,并且Angular的模板系统可以理解。第二种方法采用更强力的方法,直接在范围上设置回调结果,然后调用$scope.$digest()Angular来了解来自外部源的更改。

这两个示例不能直接比较,因为第一个示例缺少登录步骤。但是,通常需要将与此类外部API的交互封装在单独的服务中,并将结果按承诺传递给控制器​​。这样,您就可以使控制器与外部问题分开,并通过模拟服务更轻松地对其进行测试。



 类似资料:
  • 问题内容: Angularjs中angular.bind的用途是什么。请提供一个例子。无法从https://docs.angularjs.org/api/ng/function/angular.bind了解 问题答案: Angular.bind是一种实用功能,它结合了function.bind和部分功能应用程序中的功能。 绑定 (通常)是您想将当前上下文绑定到函数,但实际上在以后执行的想法。 在进

  • 我已经将ViewPager和number of Fragment实现为子级,这里每个子级都覆盖自己的。 在我的应用程序中,导航行为是随机的,不是每次都有顺序。因为页面查看器执行缓存来加载额外的子级,这就是我的问题所在。我不确定何时应该初始化/释放子类成员。 需要你们的建议,在这种情况下使用PageViwer是否更好,或者我应该为每个组件使用传统的活动流。

  • 我正在制作一个基于Java EE的产品,其中我使用了GlassFish 3和EJB 3.1。 我的应用程序有会话bean,一个调度程序,并且使用web服务。我最近了解了Apache TomEE,它支持上下文和依赖注入(CDI)。GlassFish容器也支持CDI。 如果我不需要CDI也不提供的任何特性,我可以替换会话bean吗?如果那样的话,我能得到什么好处呢?

  • 我正在尝试用Angular和一个基本的REST后端来构建一个项目的前端部分,该后端还可以处理安全问题。 我对Spring有非常基本的了解,但还没有真正使用Spring Security。 我正试图找到一种方法来构建一个安全的简单REST后端。我在网上搜索了很多,找到的主要内容是本教程:https://spring.io/guides/tutorials/spring-security-and-an

  • 问题内容: 我一直在尝试失败,在Dapper中使用with with子句已经有一段时间了。 在文档中,它确实说支持在a中使用,但我什至无法使它正常工作。 我不断收到的错误消息是Sql语法错误。 我整理了一些测试代码,希望它们能证明我正在尝试实现的目标。 问题答案: 为了执行此处需要的操作,dapper需要即时更改SQL-因此需要 真正 确保它在做正确的事情。常规有效的SQL语法包括括号: 为了消除

  • 问题内容: 我有一个包含和的临时表。它被称为。 我还有另一个包含和的表。它被称为。 我需要获取所有已完成但尚未计划的清单。 因此,我需要以某种方式从已完成的任务中清除所有行,其中都包括: 和 问题答案: 您可以使用以下命令(语法更紧凑): 或版本(尽管更复杂,但使用适当的索引应该会更有效): 当然还有@jmacinnes的答案中的版本。