数组上的JavaScript .map,如果条件满足则删除项目

时间:2014-04-26 23:20:01

标签: javascript angularjs map iteration promise

我有一个数组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 个答案:

答案 0 :(得分:1)

你已经在使用promises,你可能想要端到端地做。而且,你过早地解决了这个承诺。

假设你不希望宣传myCallFunction本身的次优案例,你仍然应该宣传它。

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;
}

注意,我们正在解决异步函数完成之后的延迟,而不是之前。

现在,我们需要实现一个“结算”功能,无论如何都会在所有承诺完成时解决。这就像$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.
             }
         });
     });
}

这种结算函数存在于大多数承诺实现中,但不存在于$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 :(得分:-1)

这是一个简洁的解决方案,可以解决非常有限的$ q的限制,而无需使用笨重的函数/ polyfill来扩充其方法。

特别是

  • $ 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,或
  • 确定所有保存后队列长度的承诺。

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

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