【问题标题】:Using $q within a callback - is this possible?在回调中使用 $q - 这可能吗?
【发布时间】:2015-07-06 22:55:35
【问题描述】:

我用下面的代码创建了一个简单的例子。目标是能够使用来自 Angular 的 $q 服务的 deferred 承诺,但在回调中拥有 $q,该回调本身返回要在主控制器中处理的结果。

我认识到错误显然是$q 的承诺需要立即返回,以便它可以“等待”结果,并且将该承诺放在回调中会阻止它立即返回。因此,下面的代码显然是一个错误的策略。

我的问题是问什么是实现与上述愿望相当的实用程序的最佳实践,包括回调的存在和需要返回一个承诺。

function asyncGreet(name, cb) {
  cb(name)
}

function okToGreet(name) {
  return name.length > 10
}

var promise = asyncGreet('Robin Hood', function(name) {
    var deferred = $q.defer();

  setTimeout(function() {
    deferred.notify('About to greet ' + name + '.');

    if (okToGreet(name)) {
      deferred.resolve('Hello, ' + name + '!');
    } else {
      deferred.reject('Greeting ' + name + ' is not allowed.');
    }
  }, 1000);

  return deferred.promise;
});

promise.then(function(greeting) {
  console.log('Success: ' + greeting);
}, function(reason) {
  console.log('Failed: ' + reason);
});

【问题讨论】:

  • asyncGreet是故意同步调用它的回调吗?
  • 不回答您的问题,但将语法 if (name.length > 10) { return true } else { return false } 替换为语法:return name.length > 10;。后者更性感:)
  • @Bergi 用例使用的是gapi,Google 的 OAuth 客户端库 (developers.google.com/api-client-library/javascript/reference/…)。我需要执行一些与该库相关的函数,其中涉及回调,然后将结果的各个方面发送到主控制器中的$scope
  • 在我看来,checkAuthhandleClientLoadhandleAuthClick 似乎都应该返回承诺。您应该 promisify gapi 方法,然后永远不要在其他任何地方使用延迟。
  • @kuanb:不,你可以使用任何适合你的 Promise 库(嘿,它们是可互操作的!)。您可能想在您的情况下使用 $q 承诺。

标签: javascript angularjs promise angular-promise


【解决方案1】:

为了整合 cmets 并解决其他答案对异步 API 的描述有些混乱,我决定提供一个答案:

如果我们假设有一些异步的非承诺/基于回调的 API,例如,asyncGreet,它可以被模拟成这样:

function asyncGreet(name, cb){
  // simulate async
  setTimeout(function(){
    someCondition ? cb({message: "Hello, " + name + "!"}) :
                    cb({error: "Greeting " + name + " is not allowed."});
  }, 2000);
}

(就本示例而言,asyncGreet 是第 3 方 API不受我们控制

要将其转换为基于 $q 承诺的 API,您将使用 $q(使用 $q.defer$q 构造函数 - 事实上,我刚刚注意到 Angular's documentation shows the $q-constructor approach)。

因此,例如,您可以创建一个greeterSvc 服务:

app.factory("greeterSvc", function greeterSvcFactory($q){
  return {
    greet: function(name){

      return $q(function(resolve, reject){            
        asyncGreet(name, function cb(data){
          if ('error' in data) {
            reject(data.error);    // extract reason
          } else {
            resolve(data.message); // extract greeting message
          }       
        });

      });
    }
  }
})

greeterSvc.greet API 的使用者然后可以.then 它并记录一些东西 - 就像你所做的一样(尽管我会使用 .catch 代替)

greeterSvc.greet("Robin Hood")
          .then(function(greeting) {
             console.log('Success: ' + greeting);
          })
          .catch(function(reason) {
             console.log('Failed: ' + reason);
          });

【讨论】:

    【解决方案2】:

    嗯,我真的想通了。您创建deferred,然后将其传递给回调。在我发布之前对我来说应该很明显,但也许它会帮助像我一样困惑的其他人:

    function asyncGreet(name, cb) {
      var deferred = $q.defer();
    
      setTimeout(function() {
        var foo = null;
        cb(name, deferred)
      }, 1000);
    
      return deferred.promise;
    }
    
    var promise = asyncGreet('Robin Hood', function(name, deferred) { 
      if (name.length > 10) {
        foo = 'Hello, ' + name + '!';
      } else {
        foo = 'Greeting ' + name + ' is not allowed.';
      }
      deferred.resolve(foo);
    });
    
    promise.then(function(greeting) {
      console.log('Success: ' + greeting);
    }, function(reason) {
      console.log('Failed: ' + reason);
    });
    

    【讨论】:

    • 这对于调用cb 函数的位置似乎有点倒退。回调通常不会同步调用(就像您在asyncGreet 中所做的那样。在您的示例中,哪个函数模拟了gapi API(因此,不受您的控制)?是asyncGreet
    • @NewDev 的目的是在问题示例代码中概述的情况参数范围内确定解决方案 - 具有回调。请参阅下面关于改进 cb 结构本身的进一步讨论的 Bergi 的 cmets。
    • 您在 SO 上提供了一个“答案”(无论您是否是 OP),这可能对未来的访问者有用。所以我的评论是让你在你的回答中解决这个问题,因为我上面提到的混乱。我认为光说是不够的 - 见其他地方的 cmets
    • 我为语气道歉 - 我无意居高临下。 “答案”一词周围的引号并不是要贬低它,而是强调我的观点,即它是“对 SO 的答案”——也就是说,它应该是完整的和独立的。这是我对您“在其他地方查看 cmets”的回复的评论,而不是对答案本身的评论。第一条评论关于答案的,我希望能推动你改进 - 但你有点忽略了它。
    • 顺便说一句,这种方法只有在 API 本身(即asyncGreet)将deferred 对象传递给回调时才有效。所以,这最终是令人困惑的。非承诺/基于回调的 API 通常没有延迟的概念,并且几乎不会将其作为参数传递给回调函数。那么,再次... asyncGreet 是在您的控制之下还是第三方库?
    猜你喜欢
    • 2016-03-18
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多