JavaScript中对数组应用.map并在满足条件时删除元素

6
我有一个名为queue的数组,当对象被修改时,我会将它们推送到这个数组中。如果用户按下save,那么我将循环遍历queue并为它们应用适当的API调用。
如果API调用成功,我希望从queue中移除该项,否则将其保留在其中,并通知用户一些项目未成功保存。我目前使用的是AngularJS。
var unsuccessfulItems = [];
var promise = queue.map(function(item) {
    var defer = $q.defer();
    myCallFunction( item
           , function( response ) {} // Success
           , function( response ) {  // Error
               unsuccessfulItems.push(item);
           }
    )
    defer.resolve();
    return defer.promise;
})
// Once all items have been processed
$q.all( promise ).then( function() {
    queue = unsuccessfulItems;
});

有没有更好的方法来做这件事?


2
似乎需要使用 filter:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter? - Sam Giles
2个回答

1
您已经在使用 Promise,也许想要全程使用。另外,您过早地解决了 Promise。
假设您不想将 myCallFunction 本身 promisify,仍然应该 promisify 它。
function myCall(item){
    var d = $q.defer();
    myCallFunction(item,function(r){ d.resolve({val:r,item:item});}
                       ,function(r){ d.reject(r);});
    return d.promise;
}

注意,我们在异步函数完成后解决延迟,而不是之前。
现在,我们需要实现一个“Settle”函数,它会在所有承诺都完成时解决,不管怎样。这就像$q.all,但会等待所有承诺解决,而不是履行。
function settle(promises){
     var d = $q.defer();
     var counter = 0;
     var results = Array(promises.length);
     promises.forEach(function(p,i){ 
         p.then(function(v){ // add as fulfilled
              results[i] = {state:"fulfilled", promise : p, value: v};
         }).catch(function(r){ // add as rejected
              results[i] = {state:"rejected", promise : p, reason: r};
         }).finally(function(){  // when any promises resolved or failed
             counter++; // notify the counter
             if (counter === promises.length) {
                d.resolve(results); // resolve the deferred.
             }
         });
     });
}

大多数 promise 实现都有这种 settle 函数,但 $q 没有。我们也可以使用拒绝和 $q.all 来实现,但这意味着异常用于流程控制,这是一种不好的做法。

现在,我们可以进行 settle 操作:

 settle(queue.map(myCall)).then(function(results){
     var failed = results.filter(function(r){ return r.state === "rejected"; });
     var failedItems = failed.map(function(i){ return i.value.item; });
 });

-1

以下是一个简洁的解决方案,它避免了非常有限的$q的限制,而不需要用庞大的函数/填充来增强其方法。

特别地,

  • $q的承诺没有包括一个简单的机制来查询它们的状态
  • $q有一个.all()方法,但没有.allSettled()。

我在这里使用的技巧是:

  • 将承诺保留在一个数组中,并在一个相应的第二个数组中记录它们(最终)的成功
  • 在成功和失败时都解决承诺,从而使$q.all()的行为类似于缺失的$q.allSettled()。
function saveQueue() {
    //First some safety
    if(queue.saving) {
        return $q.defer().resolve(-1).promise;
    }
    queue.saving = true;

    var settled = [],//the sole purpose of this array is to allow $q.all() to be called. All promises place in  this array will be resolved.
        successes = [];//an array to be (sparsely) populated with `true` for every item successfully saved. This helps overcome the lack of a simple test of a $q promise's state (pending/fulfilled/rejected).

    queue.forEach(function(item, i) {
        var defer = $q.defer(); 
        settled[i]  = defer.promise;
        myCallFunction(item, function(response) {
            //here do awesome stuff with the response
            //`item`, if required, is in scope
            successes[i] = true;//register the promise's success
            defer.resolve();//as you would expect
        }, function(error) {
            //here do awesome stuff with the error (eg log it).
            //`item`, if required, is in scope
            defer.resolve();//here we *resolve*, not reject, thus allowing `$q.all(settled)` to reflect the settling of all promises regardless of whether they were fulfilled or rejected.
        });
    });

    // Once all items have been processed
    return $q.all(settled).then(function() {
        queue = queue.filter(function(val, i) {
            return !successes[i];
        });
        queue.saving = false;
        return queue.length;
    });
}

saveQueue()将返回:

  • 如果之前的saveQueue()仍在进行中,则会返回-1的promise,或者
  • 在所有保存都完成后,队列长度的promise。

纯粹主义者无疑会认为这个解决方案是一个“反模式”(呸!),因为需要在成功和错误时都要解决promise,但问题的性质和$q的限制鼓励我们朝这个方向发展。

除此之外,您可能还需要一种机制来确保放入队列的项目是唯一的。重复的项目最多是浪费,最坏的情况可能会导致错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接