这是“延迟反模式”吗?

Posted

技术标签:

【中文标题】这是“延迟反模式”吗?【英文标题】: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(),则会出现错误

【问题讨论】:

【参考方案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 是不必要的并且容易出错。

【讨论】:

以上是关于这是“延迟反模式”吗?的主要内容,如果未能解决你的问题,请参考以下文章

扩展 UIViewController 以访问应用程序委托是一种反模式吗?

将所有文档关系的日志保存为CouchDB中的反模式吗?

这是汇编程序错误吗?绑定指令

ServiceLocator 是反模式吗?

INTERPRETER 是反模式吗?

深度嵌套字典是反模式吗?