在回调中使用$ q - 这可能吗?

时间:2015-07-06 22:55:35

标签: javascript angularjs promise angular-promise

我用下面的代码创建了一个简单的例子。目标是能够使用Angular的deferred服务中的$q承诺,但在回调中有$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);
});

2 个答案:

答案 0 :(得分: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);
});

答案 1 :(得分:2)

为了整合评论并解决其他答案对异步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是第三方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);
          });