【问题标题】:Can this promise nesting be changed to chaining?这个承诺嵌套可以改成链式吗?
【发布时间】:2015-02-20 20:11:32
【问题描述】:

这是伪场景

           | then (items)          | then (items, actions)
getItems() | getActions(for:items) | apply(actions -> items)
 :promise  |  :promise             | model <= items
           |                       |  :synchronous

言下之意:

  1. 我需要获取全局项目列表。
  2. 很好。获取的项目。
  3. 针对用户接管之前获取的项目的操作发出请求。
  4. 很好。已获取操作。
  5. 对项目列表应用操作。
  6. 将项目放在模型上并显示在视图中。

这有点像我正在使用的代码

return itemsResource
       .getItems(userId)
       .$promise
       .then(function(items) {
           return actionsResource
                  .getActions(items.map(i => i.id)) // pseudo mapper code here
                  .$promise
                  .then(function(actions) { // THIS IS NESTED so it sees both promise results
                      return [items, actions];
                  });
       })
       .then(helper.spread(function(items, actions) {
           applyActions(items, actions);
           $scope.model.items = items;
           return items;
       }));

如您所知,我最初不能使用 $q.all,因为第二个(操作)承诺取决于第一个(项目)的结果。

为什么不返回带有项目的操作?因为我正在为所有用户缓存项目,所以项目获取非常快。这类似于 Stackoverflow 的工作方式。他们只是返回问题,而不管用户是否提出请求。然后他们随后还请求首选和忽略的标签并应用于提取的问题。这可以很好地扩展,否则 SO 将需要更多的服务器来处理所有用户的初始问题列表,因为每个用户的请求都会产生不同的结果,因此缓存没有多大意义。

为什么不在获取后立即将项目应用到$scope这将消除额外的嵌套then,这是真的。但我没有这样做,因为之后还有几个步骤,并且每次承诺之一得到解决时,至少会执行一个 $digest 循环。对于许多项目(相当复杂的对象),这可以解释相当多的处理。这就是我坚持将项目传递给视图的原因,直到最后一刻。

问题

除了这两种解决方法之外,有没有办法避免嵌套的then

  1. 尽快将项目应用到$scope
  2. 将项目保存到局部变量,然后使用它

如果可能的话,我想在不引入额外资源的情况下尽可能地扁平化我的代码?

【问题讨论】:

    标签: javascript angularjs nested chaining angular-promise


    【解决方案1】:

    除非我遗漏了什么,否则这应该相当简单,不是吗?

    (为了清楚起见,我简化了一些内部函数签名)

    itemsResource.getItems(userId)
      .then(function(items) {
        return $q.all({
          items: items,
          actions: actionResource.getActions(items)
        });
      })
      .then(function(data) {
        applyActions(data.items, data.actions);
        $scope.model.items = data.items;
        return data.items;
      });
    

    plunker 用于说明

    【讨论】:

    • $q.all 会忽略非承诺的哈希值吗?我从来没有用一个对象尝试过。
    • 它只会返回(非承诺)对象。见plunker
    • 漂亮。不错的方法。
    • @BertEvans,谢谢。我认为 SO 有一个系统来表达对答案的认可;)
    • @RobertKoritnik,是的,我更喜欢哈希变体,这就是我在这里使用它的原因。
    【解决方案2】:

    如何将 items->actions 链的结果包装在一个 Promise 中?类似的东西

    return $q(function(resolve, reject) {
    
        var outerItems;
    
        itemsResource.getItems(userId).$promise
            .then(getActions)
            .then(function (actions) {
                resolve([outerItems, actions]);
            })
            .catch(function (err) { reject(err); });
    
        function getActions(items) {
            outerItems = items;
            return actionsResource.getActions(items).$promise
        }
    
    }).then(function (itemAndActions) {
        var items = itemsAndActions[0], 
            actions = itemsAndActions[1];
    
        return helper.spread(function (items, actions) {
            applyActions(items, actions);
            $scope.model.items = items;
            return items;
        })
    });
    

    【讨论】:

    • 你的代码比原来的更复杂,你不觉得吗?代码行的执行现在不是顺序的,这使得阅读和维护变得更加困难。你已经使用了局部变量,并且如果可能的话,我试图避免嵌套这两者。并且我能够从代码中消除局部变量,以降低承诺嵌套的成本。
    猜你喜欢
    • 1970-01-01
    • 2019-05-10
    • 2017-07-21
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 2017-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多