这是"Deferred Antipattern"吗?

Is this a "Deferred Antipattern"?

我发现很难理解 "deferred antipattern"。我想我在原则上理解它,但我还没有看到一个超级简单的例子来说明什么是服务,具有不同的承诺和一个具有反模式的服务,所以我想我会尝试制作自己的但看到我不是超级了解它我会先得到一些澄清。

我在工厂 (SomeFactory) 中有以下内容:

//url = 'data.json';

return {
    getData: function(){
        var deferred = $q.defer();

        $http.get(destinationFactory.url)
            .then(function (response) {

                if (typeof response.data === 'object') {
                    deferred.resolve(response.data);
                } else {
                    return deferred.reject(response.data);
                }
            })

            .catch(function (error) {
            deferred.reject(error);
        });

        return deferred.promise;
    }

我检查它的对象的原因只是为了在 $http.get()

上添加一个简单的验证层

下面,在我的指令中:

this.var = SomeFactory.getData()
    .then(function(response) {
        //some variable = response;
    })
    .catch(function(response) {
        //Do error handling here
});

根据我的理解,这是一个反模式。因为原来的 deferred promise 捕获了错误并简单地吞掉了它。它不会 return 错误,所以当调用此 "getData" 方法时,我已经做了另一个捕获来捕获错误。

如果这不是反模式,那么有人可以解释为什么两者都需要某种 "callback" 吗?当我第一次开始写这个 factory/directive 时,我预计必须在某个地方做一个延迟的承诺,但我没想到双方都必须 .catch() (也就是说我有点想我可以让工厂return 如果我做了 SomeFactory.getData()

的响应或错误

Is this a “Deferred Antipattern”?

是的,是的。 'Deferred anti-pattern' 当创建一个新的冗余延迟对象以从 promise 链 内部解析时发生。在您的情况下,您正在使用 $q 来 return 对隐含 return 承诺的承诺。您已经有一个 Promise 对象($http service 本身 return 是一个 promise),所以您只需要 return 它!

这是一个超级简单的例子,说明了一个服务,带有延迟承诺和带有反模式的服务,

This is anti-pattern

app.factory("SomeFactory",['$http','$q']){
    return {
        getData: function(){
            var deferred = $q.defer();            
            $http.get(destinationFactory.url)
              .then(function (response) {        
                 deferred.resolve(response.data);
            })
              .catch(function (error) {
                deferred.reject(error);
            });            
            return deferred.promise;
        }
     }
}])

这是你应该做的

app.factory("SomeFactory",['$http']){
    return {
        getData: function(){
           //$http itself returns a promise 
            return $http.get(destinationFactory.url);
        }
}

而两者的消费方式相同。

this.var = SomeFactory.getData()
    .then(function(response) {
        //some variable = response;
    },function(response) {
        //Do error handling here
});

这两个例子都没有问题(至少在语法上)..但第一个是多余的..不需要!

希望对您有所帮助:)

我会说这是 经典的 延迟反模式,因为您正在创建不必要的延迟对象。但是,您正在为链增加一些价值(通过您的验证)。通常,IMO,当创建延迟对象的好处很少或没有好处时,反模式特别糟糕。

所以,代码可以简单得多。

$q promise 有一些记录在案的功能,可以自动包装在 promise 中返回的任何内容(使用 $q.when)。在大多数情况下,这意味着您不必手动创建延迟:

var deferred = $q.defer();

然而,这就是文档演示如何将承诺与 $q 一起使用的方式。

因此,您可以将代码更改为:

return {
    getData: function(){
        return $http.get(destinationFactory.url)
            .then(function (response) {
                if (typeof response.data === 'object') {
                    return response.data;
                } else {
                    throw new Error('Error message here');
                }
            });

            // no need to catch and just re-throw
        });
    }

使用$q constructor是一个延迟的反模式

ANTI-PATTERN

vm.download = function() {
  var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";    
  return $q(function(resolve, reject) {    
    var req = {
      method: 'POST',
      url: url,
      responseType: 'arraybuffer'
    };   
    $http(req).then(function(response) {
      resolve(response.data);
    }, function(error) {
      reject(error);
    });
  });
}

正确

vm.download = function() {
    var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";    
    var req = {
      method: 'POST',
      url: url,
      responseType: 'arraybuffer'
    };   
    return $http(req).then(function(response) {
        return response.data;
    });
}

$http 服务已经 returns 一个承诺。使用 $q constructor 是不必要的,而且容易出错。