【问题标题】:promises only working properly with then function承诺仅与 then 功能一起正常工作
【发布时间】:2017-01-27 13:07:15
【问题描述】:

我有一个按钮,它执行一个带有承诺的功能,该功能可以从 html 中的 firebase 获取和显示数据(我正在使用 angularJS、ionic 和 firebase)。

问题是:如果我不包含 .then(function(){}) 之后,promise 将以异步方式执行,这意味着我必须再次单击按钮,以便数据以 html 显示.

我想将数据放在 promise 之后的范围内(从 firebase 获取数据),但由于某种原因,只有在我在其后放置一个 .then 函数时才有效。

但是,数据在控制台中正常显示,但不是在 html 中(这意味着我认为该函数没有附加到范围)。

这是一段代码:

$scope.displayChat = function () {
    var userId = firebase.auth().currentUser.uid; // Get ID
    var deferred = $q.defer()

    var db = firebase.database();
    var ref = db.ref("12346787");

  ref.on("value", function(snapshot) {
           console.log(snapshot.val());
           $scope.chatArray = snapshot.val();
           deferred.resolve()

       }, function (errorObject) {
           console.log("The read failed: " + errorObject.code);
       })

   return deferred.promise.then(function(){
        // Removing this empty .then(function(){}) function
        // will result in asynchronousity.
        // just "return deferred.promise;" doesn't work.
    })
}

有什么解决办法吗?我对承诺没有太多经验,但我没有找到任何相关的东西。干杯。

【问题讨论】:

  • 为什么需要承诺?你注意到解决任何问题。 deferred.resolve() --> 不解析任何值。

标签: javascript angularjs firebase ionic-framework firebase-realtime-database


【解决方案1】:

promise的目的是管理异步方法,所以我不是很明白这个问题...

此外,通常必须执行 displayChat 中的代码,只有回调必须执行。您应该返回承诺,一旦您确定所需的异步方法已完成,您就可以执行回调。

【讨论】:

    【解决方案2】:

    当 AngularJS 框架外部的事件对范围进行更改时,框架需要执行 $apply 来启动摘要循环以更新 DOM。


    (来源:angularjs.org

    $q 服务承诺的.then 方法会自动启动必要的摘要循环。在这种情况下,displayChat 函数返回的承诺将被丢弃,并且不会启动摘要循环。随后单击该按钮将启动摘要循环。

    将来,有人可能希望链接displayChat 函数返回的承诺。我建议通过返回适当的承诺并将任何对范围的更改移动到 .then 方法中来使函数更加通用。

    $scope.displayChat = function () {
        var userId = firebase.auth().currentUser.uid; // Get ID
        var deferred = $q.defer()
    
        var db = firebase.database();
        var ref = db.ref("12346787");
    
        //ref.on("value", function(snapshot) {
        //USE once
        ref.once("value", function(snapshot) {
               console.log(snapshot.val());
               //$scope.chatArray = snapshot.val();
               deferred.resolve(snapshot.val());
    
           }, function (errorObject) {
               console.log("The read failed: " + errorObject.code);
               //ADD rejection case
               deferred.reject(errorObject);
           })
    
       return deferred.promise.then(function(chatArray){
           //Move scope changes here
           $scope.chatArray = chatArray;
           //RETURN to chain data
           return chatArray;
    
           // Removing this empty .then(function(){}) function
           // will result in asynchronousity.
           // just "return deferred.promise;" doesn't work.
       })
    }
    

    另外,为了避免内存泄漏,请使用ref.once 而不是ref.on,并确保在错误情况下拒绝承诺。

    【讨论】:

      【解决方案3】:

      Promises 用于延迟某些逻辑的执行,直到 promise 得到满足 - 即:您已从数据库收到结果。在您的情况下,您已经在 ref.on 中推迟了控制台显示和 $scope 变量的设置。承诺代码是多余的。

      您的结果显示在控制台中的事实证明您已收到结果。当您更新范围中的数据时,它不会出现,直到一个摘要周期发生。大多数时候,Angular 可以自动判断何时需要运行摘要循环。在没有的情况下,您可以通过在超时中包装与范围相关的逻辑来强制它,在这种情况下,您的代码将如下所示:

      $scope.displayChat = function () {
      var userId = firebase.auth().currentUser.uid; // Get ID
      
      var db = firebase.database();
      var ref = db.ref("12346787");
      
      ref.on("value", function(snapshot) {
             console.log(snapshot.val());
             $timeout(function () {
                  $scope.chatArray = snapshot.val();
             });
      
         }, function (errorObject) {
             console.log("The read failed: " + errorObject.code);
         })
      

      }

      您使用 promise .then 方法恰好触发了一个摘要循环。承诺本身真的没有做任何事情。

      如果您的意图是在快照可用时将其传回给调用者,那么 Promise 就会发挥作用,并按如下方式完成:

          $scope.displayChat = function () {
          var userId = firebase.auth().currentUser.uid; // Get ID
          var deferred = $q.defer()
      
          var db = firebase.database();
          var ref = db.ref("12346787");
      
          ref.on("value", function(snapshot) {
                  deferred.resolve(snapshot)
      
             }, function (errorObject) {
                 console.log("The read failed: " + errorObject.code);
             })
      
         return deferred.promise;
      };
      
      $scope.callDisplayChat = function () {
          $scope.displayChat().then(function (result) {
              $scope.chatArray = result.val();
          });
      };
      

      【讨论】:

      • 我明白了,非常感谢!由于我的朋友设置了这个并且他不知道为什么会发生这种情况,所以我在这里问了它。我有点知道/听说过 $scope.digest() 和 $scope.apply(),但并不真正知道它们是如何工作的,所以非常感谢:D
      • @Pokfin - 随时!如果这解决了您的问题,请务必将其作为已接受的答案。仅供参考...您可以在此处找到有关 $scope.apply 与 $timeout 的更多信息 - stackoverflow.com/questions/23070822/…
      猜你喜欢
      • 2016-09-05
      • 1970-01-01
      • 1970-01-01
      • 2019-08-25
      • 2021-05-01
      • 2012-12-29
      • 1970-01-01
      • 1970-01-01
      • 2016-02-15
      相关资源
      最近更新 更多