【问题标题】:Is this a "Deferred Antipattern"?这是“延迟反模式”吗?
【发布时间】:2015-08-25 08:14:24
【问题描述】:

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

我在工厂(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
});

现在据我了解,这是一种反模式。因为最初的延迟承诺会捕获错误并简单地吞下它。它不会返回错误,因此当调用此“getData”方法时,我会再次捕获错误。

如果这不是反模式,那么有人可以解释为什么两者都需要某种“回调”吗?当我第一次开始编写这个工厂/指令时,我预计必须在某个地方做一个延迟承诺,但我没有预料到双方都必须.catch()(也就是我想我可以让工厂返回响应或如果我做了SomeFactory.getData(),则会出现错误

【问题讨论】:

    标签: angularjs angularjs-directive angularjs-service angular-promise


    【解决方案1】:

    这是“延迟反模式”吗?

    是的,是的。 “延迟反模式”在创建新的冗余延迟对象以从承诺链内部解决时发生。在您的情况下,您使用 $q 来返回隐含返回承诺的承诺。你已经有一个 Promise 对象($http service 本身返回一个promise),所以你只需要返回它!

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

    这是反模式

    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
    });
    

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

    希望对你有帮助:)

    【讨论】:

    • 您好 NLN,感谢您的回复。因此,在您的示例中,是否包含typeof 验证,或者在从工厂检索后在控制器中完成?
    • 嗨阿列斯基。您可以在.success() 函数中执行typeof 验证:)
    • 我在未登录的情况下进行了编辑,现在它已被锁定。基本上, .success 和 .error 已被弃用。您现在可以使用 .then。
    • @SenHeng:你的编辑看起来不错(而且很有必要,有点),谢谢 :)
    • 我不认为第一个是多余的并且不需要。可能您想在工厂中操作数据并在那里保存其他控制器共享的值...
    【解决方案2】:

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

    因此,代码可以简单得多。

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

    var deferred = $q.defer();
    

    但是,这就是文档演示如何将 Promise 与 $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
            });
        }
    

    【讨论】:

    • 好吧,因为添加了我的光验证,我应该离开“回调”-y 然后部分,因为我在返回它之前对它做一些事情?在使用 Promise 之前在 Promise 中做一些工作有多常见?我更喜欢 NLN 的回答,但他不得不放弃我的验证......
    • 从 Promise 链中读取数据,做一些逻辑然后返回数据进行进一步处理是很常见的。这就是使它成为 chain 的原因:)。最后,您到达终点并将数据绑定到$scope 或任何地方。
    • 所以这里的问题真的是让$q 不必要地参与吗?
    • 是的,你可以想象你有一个多层的承诺链。为链中的每个链接创建一个deferred 会很麻烦,而且没有必要。
    【解决方案3】:

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

    反模式

    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 服务已经返回了一个承诺。使用$q constructor 是不必要的并且容易出错。

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 2011-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-30
      • 1970-01-01
      相关资源
      最近更新 更多