【问题标题】:How to execute promises "sync" and not in async way如何执行承诺“同步”而不是异步方式
【发布时间】:2015-09-15 03:50:44
【问题描述】:

我调用 getBubblesUserAccess 返回以特殊方式排序的 json 对象。结果我想运行一个 foreach 并获取其他消息,但我想按“顺序”返回它们。我知道它将运行这些异步,但必须有一种方法可以强制它“顺序”执行。 (以上代码是我最后一次尝试添加延迟...)

示例 伪代码 - 获取我的群组

{  
  "id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
  "parent":"53750396-7d26-41f3-913d-1b93276b9e09",
  "name":"XX",
  "createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
  "hasWriteAccess":true,
  "hasCreateAccess":false,
  "hasDeleteAccess":false,
  "hasAdminAccess":false,
  "settingsBubbleId":"00000000-0000-0000-0000-000000000000"
},
{  
  "id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
  "parent":"53750396-7d26-41f3-913d-1b93276b9e09",
  "name":"XX",
  "createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
  "hasWriteAccess":true,
  "hasCreateAccess":false,
  "hasDeleteAccess":false,
  "hasAdminAccess":false,
  "settingsBubbleId":"00000000-0000-0000-0000-000000000000"
}

从这个结果中,我想遍历那些父 id 字符串并调用另一个与之响应的服务。

伪代码 对于上面的每个组,调用另一个具有父 ID 的服务并获得结果。这个结果将被添加到一个新的 JSON 对象中。

"messages":[  
  {  
     "id":"f1d1aeda-d4e2-4563-85d5-d954c335b31c",
     "text":"asd",
     "sent":"2015-09-10T22:31:09.897+00:00",
     "sender":"6b9e404b-ef37-4d07-9267-3e7b2579003b",
     "senderName":"XXX XXXX"
  },
  {  
     "id":"a7ac0432-e945-440e-91ce-185170cbf3de",
     "text":"asd",
     "sent":"2015-09-10T22:28:24.383+00:00",
     "sender":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
     "senderName":"ZZZZZ ZZZZ"
  },

我的问题是我的第二个 foreach 正在异步运行(应该如此),我希望它以与第一个 json 对象相同的顺序解析...

我的代码::

var loadBubblesAccess = function () {
        if (vm.running && angular.isDefined(vm.running)) { return; }
        vm.running = true;
        vm.bubblesWithMessages = null;

        return BubbleFactory.getBubblesUserAccess().then(function (bubblesAccessTo) {
            return bubblesAccessTo;
        });
    },
        loadSubBubbles = function (bubblesAccessTo) {

            /**
             * Result from chain method with all bubbles user has access to.
             */

            var promiseArray = [];
            //var promiseArrayError = [];
            var i = 0;
            /**
             * Creates a defer object so that we will not resolve before for each loop has been gone thru.. async problems.
             */
            var deferred = $q.defer();

            angular.forEach(bubblesAccessTo, function (bubble) {
                $log.error(JSON.stringify(bubblesAccessTo));
                /**
                 * Get 20 because default thats default and cache and e-tags are done to that number..
                 */
                BubbleFactory.getBubbleMessages(bubble.id, 0, 20, false).then(function (data) {
                        i++;
                        if (data.messages.length > 0) {
                            promiseArray.push({ bubbleSortOrder: i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: smartTrim(data.messages[0].text, 400, ' ', ' ...'), bubbleMessagesSent: data.messages[0].sent });
                        }
                        else {
                            // console.log("YYYY::: " + bubble.parent);
                            promiseArray.push({ bubbleSortOrder:i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: 'Inget meddelande än..', bubbleMessagesSent: '' });
                        }

                    });

                    /**
                     * Check if we have gone thru all bubbles - when finished we resolve defer object.
                     */
                    if(i===bubblesAccessTo.length)
                    {
                        deferred.resolve(promiseArray);
                    }

            });
            //$log.debug.log(promiseArray);
            vm.bubblesWithMessages = promiseArray;
            promiseArray.length = 0;
            vm.running = false;
        };



    loadBubblesAccess().then(loadSubBubbles);

【问题讨论】:

  • 如果你做了一个promise数组然后使用$q.all(),结果应该在.then().then()中的正确顺序$q.all
  • 我尝试使用循环 foreach 并将它们添加到 promiseArray 的方法,然后在 q$.all 中执行它们,但结果相同。 :( 你确定它会用 q.all 以正确的顺序执行它们吗?
  • 单个回调总是会出现问题,你必须从 .all() 的单个回调中获取数据
  • 您可以使用[].reduce 按顺序浏览它们,等待一个完成后再调用下一个。查看本文中的“Collection Kerfuffle”部分:taoofcode.net/promise-anti-patterns
  • 我认为我应该使用 .spread - 我调用每个函数并返回结果,然后我会将结果合并到一个 json 数组中,然后发送到控制器。因为其中一件事是使用 $q.all 我得到了结果,但我也想从第一个结果中获得信息。我回家后会尝试这种方法。q.all([loadSomething(), loadAnotherThing()]) .spread(function(something, another) { DoSomethingOnThem(something, another); });

标签: angularjs promise q


【解决方案1】:

AngularJS 中的$q 服务被描述为“轻量级”,因为它只实现了 90% 的人需要的功能。这使得它的代码量很小——代价是不能很容易地处理像你这样的请求。

如果您可以选择,请尝试使用 bluebird 等替代方法。 Bluebird 提供了一个reduce() 函数,它可以串行执行一组promise,并按照请求的顺序返回它们的结果。它使这项任务变得简单,因为您的结果数组将匹配您的数据数组,并且您可以非常轻松地匹配结果。

如果你没有那个选项,有一个标准的(如果不是完全简单的)承诺技术,你可以在其中构建一个你想要承诺的元素数组,然后调用处理函数(返回一个承诺)在第一个值上(从数组中弹出)。在.finally()处理程序中,使用下一个值递归调用处理函数,直到它为空(或发生错误)。

这个的伪代码:

var valuesToProcess = [1, 2, 3],
    results = [];

function processValue(val) {
    myProcessingFunction(val).then(function(result) {
        results.push(result);
    }).catch(function(e) {
        console.log('FAIL!', e);
    }).finally(function() {
        if (valuesToProcess.length > 0) {
            processValue(valuesToProcess.shift());
        } else {
            // All done - do something with results here
        }
    });
}

// Note: No error checking done, assumes we have work to do...
processValue(valuesToProcess.shift());

您需要根据您的用例对其进行调整,但这是一种保证串行操作和结果处理的简单技术。

【讨论】:

  • 我最终需要在后端 API 中修复它,因为我的解决方案是在前端进行大量数组操作,我不喜欢这段代码混乱.. :( 我猜@chad -robinson 和 kevin-b 你的回答会做到的。但我真的不喜欢做很多数组处理:(
  • 但我想这不是我最后一次遇到这个问题 - 如果我将来遇到类似问题,我会添加书签并返回。 :)
猜你喜欢
  • 2021-07-16
  • 1970-01-01
  • 2017-12-18
  • 1970-01-01
  • 1970-01-01
  • 2021-01-22
  • 1970-01-01
  • 2018-12-22
相关资源
最近更新 更多