你能在返回之前解决一个 angularjs 承诺吗?

Posted

技术标签:

【中文标题】你能在返回之前解决一个 angularjs 承诺吗?【英文标题】:Can you resolve an angularjs promise before you return it? 【发布时间】:2013-12-31 13:41:55 【问题描述】:

我正在尝试编写一个返回承诺的函数。但有时要求的信息会立即可用。我想把它包装在一个承诺中,这样消费者就不需要做出决定了。

function getSomething(id) 
    if (Cache[id]) 
        var deferred = $q.defer();
        deferred.resolve(Cache[id]); // <-- Can I do this?
        return deferred.promise;
     else 
        return $http.get('/someUrl', id:id);
    

并像这样使用它:

somethingService.getSomething(5).then(function(thing) 
    alert(thing);
);

问题是回调没有为预先解决的承诺执行。这是合法的做法吗?有没有更好的方法来处理这种情况?

【问题讨论】:

在第一种情况下编写返回的更简单的方法是return $q.when(Cache[id])。无论如何,这应该可以工作并每次都调用回调,因为您每次都在创建新的 Promise。 工作:plnkr.co/edit/OGO8T2M1fE3Mrgj2oozj?p=preview 粗鲁。我生命中的一个小时失去了。我在单元测试中尝试了这个,并且在测试完成后实现了承诺,但我没有看到它。我的测试有问题,而不是代码。 确保调用 $scope.$apply() 以确保在测试期间问题立即解决。 我认为 httpbackend.flush 说明了这一点,但 $q 可能没有。我没有在这个测试中使用范围。我正在直接测试服务,但我还是让它工作了,谢谢。 【参考方案1】:

如何在 Angular 1.x 中简单地返回一个预先解决的承诺

已解决的承诺:

return $q.when( someValue );    // angular 1.2+
return $q.resolve( someValue ); // angular 1.4+, alias to `when` to match ES6

被拒绝的承诺:

return $q.reject( someValue );

【讨论】:

不需要那个工厂,那些辅助函数已经可用:resolved: $q.when, rejected: $q.reject 嗨,Bergi,感谢您的宝贵贡献。我已经相应地编辑了我的答案。 我觉得应该选择这个答案。 @mortezaT 如果它被选中,它不会给我一个金徽章。 ;)【参考方案2】:

简短回答:是的,您可以在返回 AngularJS 承诺之前解决它,它的行为会如您所愿。

来自JB Nizet's Plunkr,但经过重构以在最初询问的内容(即对服务的函数调用)和实际在现场的上下文中工作。

服务内部...

function getSomething(id) 
    // There will always be a promise so always declare it.
    var deferred = $q.defer();
    if (Cache[id]) 
        // Resolve the deferred $q object before returning the promise
        deferred.resolve(Cache[id]); 
        return deferred.promise;
     
    // else- not in cache 
    $http.get('/someUrl', id:id).success(function(data)
        // Store your data or what ever.... 
        // Then resolve
        deferred.resolve(data);               
    ).error(function(data, status, headers, config) 
        deferred.reject("Error: request returned status " + status); 
    );
    return deferred.promise;


在控制器内部....

somethingService.getSomething(5).then(    
    function(thing)      // On success
        alert(thing);
    ,
    function(message)    // On failure
        alert(message);
    
);

我希望它对某人有所帮助。我没有发现其他答案很清楚。

【讨论】:

如果http GET失败,返回的promise不会这样拒绝。 所以这篇文章的 tl;dr 是:是的,你可以在返回之前解决一个 Promise,它会按预期短路。 这个答案也适用于 Angular 的承诺所基于的 Kris Kowal 的 Q。 我在您的回答中添加了一个错误处理示例,希望没问题。 其实这个版本比原版差;当他可以使用 $http 时,不需要创建另一个延迟/承诺,就像原始答案中所做的那样。【参考方案3】:

我喜欢使用工厂从我的资源中获取数据。

.factory("SweetFactory", [ "$http", "$q", "$resource", function( $http, $q, $resource ) 
    return $resource("/sweet/app", , 
        "put": 
            method: "PUT",
            isArray: false
        ,"get": 
            method: "GET",
            isArray: false
        
    );
]);

然后在这里像这样在服务中公开我的模型

 .service("SweetService",  [ "$q", "$filter",  "$log", "SweetFactory",
    function ($q, $filter, $log, SweetFactory) 

        var service = this;

        //Object that may be exposed by a controller if desired update using get and put methods provided
        service.stuff=
            //all kinds of stuff
        ;

        service.listOfStuff = [
            value:"", text:"Please Select",
            value:"stuff", text:"stuff"];

        service.getStuff = function () 

            var deferred = $q.defer();

          var promise = SweetFactory.get().$promise.then(
                function (response) 
                    if (response.response.result.code !== "COOL_BABY") 
                        deferred.reject(response);
                     else 
                        deferred.resolve(response);
                        console.log("stuff is got", service.alerts);
                        return deferred.promise;
                    

                
            ).catch(
                function (error) 
                    deferred.reject(error);
                    console.log("failed to get stuff");
                
            );

            promise.then(function(response)
                //...do some stuff to sett your stuff maybe fancy it up
                service.stuff.formattedStuff = $filter('stuffFormatter')(service.stuff);

            );


            return service.stuff;
        ;


        service.putStuff = function () 
            console.log("putting stuff eh", service.stuff);

            //maybe do stuff to your stuff

            AlertsFactory.put(service.stuff).$promise.then(function (response) 
                console.log("yep yep", response.response.code);
                service.getStuff();
            ).catch(function (errorData) 
                alert("Failed to update stuff" + errorData.response.code);
            );

        ;

    ]);

然后我的控制器可以简单地通过引用注入的 Service.whatever 来包含它并公开它或在其上下文中做正确的事情

似乎工作正常。但我对角度有点陌生。 *为了清晰起见,大部分都省略了错误处理

【讨论】:

您的getStuff 方法正在使用deferred antipattern【参考方案4】:

如果我想在数组或对象中实际缓存数据,我通常会这样做

app.factory('DataService', function($q, $http) 
  var cache = ;
  var service=        
    getData: function(id, callback) 
      var deffered = $q.defer();
      if (cache[id])          
        deffered.resolve(cache[id])
       else             
        $http.get('data.json').then(function(res) 
          cache[id] = res.data;              
          deffered.resolve(cache[id])
        )
      
      return deffered.promise.then(callback)
    
  

  return service

)

DEMO

【讨论】:

【参考方案5】:

你忘记初始化缓存元素

function getSomething(id) 
    if (Cache[id]) 
        var deferred = $q.defer();
        deferred.resolve(Cache[id]); // <-- Can I do this?
        return deferred.promise;
     else 
        Cache[id] = $http.get('/someUrl', id:id);
        return Cache[id];
    

【讨论】:

对不起。确实如此。为了清楚问题,我试图简化代码。即便如此,如果它进入预先解决的承诺,它似乎并没有调用回调。 我不认为如果你用一个 Promise 来解决一个 Promise,那么内部的 Promise 就会变平。这将使用承诺而不是预期对象填充Cache,并且对象在缓存中和不在缓存中的情况的返回类型将不同。这更正确,我认为:$http.get('/someUrl', id: id).then(function (response) Cache[id] = response.data; return Cache[id]; );

以上是关于你能在返回之前解决一个 angularjs 承诺吗?的主要内容,如果未能解决你的问题,请参考以下文章

你能“累积”承诺结果形成一连串“那么”吗? [复制]

你能在三元中有一个返回和一个公式吗?

AngularJS 测试 - 来自服务的承诺无法解决

你能在文本中隐藏数据吗?

你能在 CSS“内容”属性中使用 HTML 实体吗? [复制]

Anylogic:你能在编辑框中输入概率分布吗?